diff --git a/BUILD_OSX.md b/BUILD_OSX.md
index 44f27d3d02..1c9c5a9796 100644
--- a/BUILD_OSX.md
+++ b/BUILD_OSX.md
@@ -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.
diff --git a/cmake/externals/neuron/CMakeLists.txt b/cmake/externals/neuron/CMakeLists.txt
index 6936725571..76dda8f8c5 100644
--- a/cmake/externals/neuron/CMakeLists.txt
+++ b/cmake/externals/neuron/CMakeLists.txt
@@ -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}
diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json
index bad24dd3a1..59fa9c4f3d 100644
--- a/domain-server/resources/describe-settings.json
+++ b/domain-server/resources/describe-settings.json
@@ -1,5 +1,5 @@
{
- "version": 1.4,
+ "version": 1.5,
"settings": [
{
"name": "metaverse",
@@ -116,6 +116,7 @@
"name": "hosts",
"label": "Hosts",
"type": "table",
+ "can_add_new_rows": true,
"help": "Usernames of hosts who can reliably show your domain to new visitors.",
"numbered": false,
"columns": [
@@ -130,6 +131,7 @@
"name": "tags",
"label": "Tags",
"type": "table",
+ "can_add_new_rows": true,
"help": "Common categories under which your domain falls.",
"numbered": false,
"columns": [
@@ -139,6 +141,218 @@
"can_set": true
}
]
+ },
+ {
+ "label": "Operating Hours",
+ "help": "\"Open\" domains can be searched using their operating hours. Hours are entered in the local timezone, selected below.",
+
+ "name": "weekday_hours",
+ "caption": "Weekday Hours (Monday-Friday)",
+ "type": "table",
+ "can_add_new_rows": false,
+ "columns": [
+ {
+ "name": "open",
+ "label": "Opening Time",
+ "type": "time",
+ "default": "00:00",
+ "editable": true
+ },
+ {
+ "name": "close",
+ "label": "Closing Time",
+ "type": "time",
+ "default": "23:59",
+ "editable": true
+ }
+ ]
+ },
+ {
+ "name": "weekend_hours",
+ "label": "Weekend Hours (Saturday/Sunday)",
+ "type": "table",
+ "can_add_new_rows": false,
+ "columns": [
+ {
+ "name": "open",
+ "label": "Opening Time",
+ "type": "time",
+ "default": "00:00",
+ "editable": true
+ },
+ {
+ "name": "close",
+ "label": "Closing Time",
+ "type": "time",
+ "default": "23:59",
+ "editable": true
+ }
+ ]
+ },
+ {
+ "label": "Time Zone",
+ "name": "utc_offset",
+ "caption": "Time Zone",
+ "help": "This server's time zone. Used to define your server's operating hours.",
+ "type": "select",
+ "options": [
+ {
+ "value": "-12",
+ "label": "UTC-12:00"
+ },
+ {
+ "value": "-11",
+ "label": "UTC-11:00"
+ },
+ {
+ "value": "-10",
+ "label": "UTC-10:00"
+ },
+ {
+ "value": "-9.5",
+ "label": "UTC-09:30"
+ },
+ {
+ "value": "-9",
+ "label": "UTC-09:00"
+ },
+ {
+ "value": "-8",
+ "label": "UTC-08:00"
+ },
+ {
+ "value": "-7",
+ "label": "UTC-07:00"
+ },
+ {
+ "value": "-6",
+ "label": "UTC-06:00"
+ },
+ {
+ "value": "-5",
+ "label": "UTC-05:00"
+ },
+ {
+ "value": "-4",
+ "label": "UTC-04:00"
+ },
+ {
+ "value": "-3.5",
+ "label": "UTC-03:30"
+ },
+ {
+ "value": "-3",
+ "label": "UTC-03:00"
+ },
+ {
+ "value": "-2",
+ "label": "UTC-02:00"
+ },
+ {
+ "value": "-1",
+ "label": "UTC-01:00"
+ },
+ {
+ "value": "",
+ "label": "UTC±00:00"
+ },
+ {
+ "value": "1",
+ "label": "UTC+01:00"
+ },
+ {
+ "value": "2",
+ "label": "UTC+02:00"
+ },
+ {
+ "value": "3",
+ "label": "UTC+03:00"
+ },
+ {
+ "value": "3.5",
+ "label": "UTC+03:30"
+ },
+ {
+ "value": "4",
+ "label": "UTC+04:00"
+ },
+ {
+ "value": "4.5",
+ "label": "UTC+04:30"
+ },
+ {
+ "value": "5",
+ "label": "UTC+05:00"
+ },
+ {
+ "value": "5.5",
+ "label": "UTC+05:30"
+ },
+ {
+ "value": "5.75",
+ "label": "UTC+05:45"
+ },
+ {
+ "value": "6",
+ "label": "UTC+06:00"
+ },
+ {
+ "value": "6.5",
+ "label": "UTC+06:30"
+ },
+ {
+ "value": "7",
+ "label": "UTC+07:00"
+ },
+ {
+ "value": "8",
+ "label": "UTC+08:00"
+ },
+ {
+ "value": "8.5",
+ "label": "UTC+08:30"
+ },
+ {
+ "value": "8.75",
+ "label": "UTC+08:45"
+ },
+ {
+ "value": "9",
+ "label": "UTC+09:00"
+ },
+ {
+ "value": "9.5",
+ "label": "UTC+09:30"
+ },
+ {
+ "value": "10",
+ "label": "UTC+10:00"
+ },
+ {
+ "value": "10.5",
+ "label": "UTC+10:30"
+ },
+ {
+ "value": "11",
+ "label": "UTC+11:00"
+ },
+ {
+ "value": "12",
+ "label": "UTC+12:00"
+ },
+ {
+ "value": "12.75",
+ "label": "UTC+12:45"
+ },
+ {
+ "value": "13",
+ "label": "UTC+13:00"
+ },
+ {
+ "value": "14",
+ "label": "UTC+14:00"
+ }
+ ]
}
]
},
diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js
index aecc48b31f..c2cb2ecb80 100644
--- a/domain-server/resources/web/settings/js/settings.js
+++ b/domain-server/resources/web/settings/js/settings.js
@@ -243,6 +243,16 @@ $(document).ready(function(){
}
});
+ $('#' + Settings.FORM_ID).on('change', 'input.table-time', function() {
+ // Bootstrap switches in table: set the changed data attribute for all rows in table.
+ var row = $(this).closest('tr');
+ if (row.hasClass("value-row")) { // Don't set attribute on input row switches prior to it being added to table.
+ row.find('td.' + Settings.DATA_COL_CLASS + ' input').attr('data-changed', true);
+ updateDataChangedForSiblingRows(row, true);
+ badgeSidebarForDifferences($(this));
+ }
+ });
+
$('.advanced-toggle').click(function(){
Settings.showAdvanced = !Settings.showAdvanced
var advancedSelector = $('.' + Settings.ADVANCED_CLASS)
@@ -987,7 +997,7 @@ function makeTable(setting, keypath, setting_value, isLocked) {
html += "
" + rowIndexOrName + " | "
}
- var isNonDeletableRow = false;
+ var isNonDeletableRow = !setting.can_add_new_rows;
_.each(setting.columns, function(col) {
@@ -1007,6 +1017,10 @@ function makeTable(setting, keypath, setting_value, isLocked) {
html += ""
+ " | ";
+ } else if (isArray && col.type === "time" && col.editable) {
+ html += ""
+ + " | ";
} else {
// Use a hidden input so that the values are posted.
html += ""
@@ -1196,15 +1210,21 @@ function addTableRow(add_glyphicon) {
// Hide inputs
var input = $(element).find("input")
var isCheckbox = false;
+ var isTime = false;
if (input.hasClass("table-checkbox")) {
input = $(input).parent();
isCheckbox = true;
+ } else if (input.hasClass("table-time")) {
+ input = $(input).parent();
+ isTime = true;
}
var val = input.val();
if (isCheckbox) {
- val = $(input).find("input").is(':checked');
// don't hide the checkbox
+ val = $(input).find("input").is(':checked');
+ } else if (isTime) {
+ // don't hide the time
} else {
input.attr("type", "hidden")
}
diff --git a/domain-server/src/DomainMetadata.cpp b/domain-server/src/DomainMetadata.cpp
index 26d2bb87ce..c5048ea9d8 100644
--- a/domain-server/src/DomainMetadata.cpp
+++ b/domain-server/src/DomainMetadata.cpp
@@ -10,16 +10,18 @@
#include "DomainMetadata.h"
-#include
+#include
#include
+#include
#include
+#include "DomainServer.h"
#include "DomainServerNodeData.h"
const QString DomainMetadata::USERS = "users";
-const QString DomainMetadata::USERS_NUM_TOTAL = "num_users";
-const QString DomainMetadata::USERS_NUM_ANON = "num_anon_users";
-const QString DomainMetadata::USERS_HOSTNAMES = "user_hostnames";
+const QString DomainMetadata::Users::NUM_TOTAL = "num_users";
+const QString DomainMetadata::Users::NUM_ANON = "num_anon_users";
+const QString DomainMetadata::Users::HOSTNAMES = "user_hostnames";
// users metadata will appear as (JSON):
// { "num_users": Number,
// "num_anon_users": Number,
@@ -27,26 +29,30 @@ const QString DomainMetadata::USERS_HOSTNAMES = "user_hostnames";
// }
const QString DomainMetadata::DESCRIPTORS = "descriptors";
-const QString DomainMetadata::DESCRIPTORS_DESCRIPTION = "description";
-const QString DomainMetadata::DESCRIPTORS_CAPACITY = "capacity"; // parsed from security
-const QString DomainMetadata::DESCRIPTORS_RESTRICTION = "restriction"; // parsed from ACL
-const QString DomainMetadata::DESCRIPTORS_MATURITY = "maturity";
-const QString DomainMetadata::DESCRIPTORS_HOSTS = "hosts";
-const QString DomainMetadata::DESCRIPTORS_TAGS = "tags";
+const QString DomainMetadata::Descriptors::DESCRIPTION = "description";
+const QString DomainMetadata::Descriptors::CAPACITY = "capacity"; // parsed from security
+const QString DomainMetadata::Descriptors::RESTRICTION = "restriction"; // parsed from ACL
+const QString DomainMetadata::Descriptors::MATURITY = "maturity";
+const QString DomainMetadata::Descriptors::HOSTS = "hosts";
+const QString DomainMetadata::Descriptors::TAGS = "tags";
+const QString DomainMetadata::Descriptors::HOURS = "hours";
+const QString DomainMetadata::Descriptors::Hours::WEEKDAY = "weekday";
+const QString DomainMetadata::Descriptors::Hours::WEEKEND = "weekend";
+const QString DomainMetadata::Descriptors::Hours::UTC_OFFSET = "utc_offset";
+const QString DomainMetadata::Descriptors::Hours::OPEN = "open";
+const QString DomainMetadata::Descriptors::Hours::CLOSE = "close";
// descriptors metadata will appear as (JSON):
-// { "capacity": Number,
-// TODO: "hours": String, // UTF-8 representation of the week, split into 15" segments
+// { "description": String, // capped description
+// "capacity": Number,
// "restriction": String, // enum of either open, hifi, or acl
// "maturity": String, // enum corresponding to ESRB ratings
// "hosts": [ String ], // capped list of usernames
-// "description": String, // capped description
-// TODO: "img": {
-// "src": String,
-// "type": String,
-// "size": Number,
-// "updated_at": Number,
-// },
// "tags": [ String ], // capped list of tags
+// "hours": {
+// "utc_offset": Number,
+// "weekday": [ { "open": Time, "close": Time } ],
+// "weekend": [ { "open": Time, "close": Time } ],
+// }
// }
// metadata will appear as (JSON):
@@ -54,36 +60,167 @@ const QString DomainMetadata::DESCRIPTORS_TAGS = "tags";
//
// it is meant to be sent to and consumed by an external API
-DomainMetadata::DomainMetadata() {
- _metadata[USERS] = {};
- _metadata[DESCRIPTORS] = {};
+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 {
+ QVariantList{ QVariant{}, QVariant{} } }
+ },
+ { Descriptors::Hours::WEEKEND, QVariantList {
+ QVariantList{ QVariant{}, QVariant{} } }
+ }
+ }
+ } };
+
+ assert(dynamic_cast(domainServer));
+ DomainServer* server = static_cast(domainServer);
+
+ // update the metadata when a user (dis)connects
+ connect(server, &DomainServer::userConnected, this, &DomainMetadata::usersChanged);
+ connect(server, &DomainServer::userDisconnected, this, &DomainMetadata::usersChanged);
+
+ // update the metadata when security changes
+ connect(&server->_settingsManager, &DomainServerSettingsManager::updateNodePermissions,
+ this, static_cast(&DomainMetadata::securityChanged));
+
+ // initialize the descriptors
+ securityChanged(false);
+ descriptorsChanged();
}
-void DomainMetadata::setDescriptors(QVariantMap& settings) {
+QJsonObject DomainMetadata::get() {
+ maybeUpdateUsers();
+ return QJsonObject::fromVariantMap(_metadata);
+}
+
+QJsonObject DomainMetadata::get(const QString& group) {
+ maybeUpdateUsers();
+ 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;
+
+ assert(target.canConvert());
+ auto& targetList = *static_cast(target.data());
+
+ // if/when multiple ranges are allowed, this list will need to be iterated
+ assert(targetList[0].canConvert());
+ auto& hours = *static_cast(targetList[0].data());
+
+ auto deltaHours = delta.toList()[0].toMap();
+ if (deltaHours.isEmpty()) {
+ return;
+ }
+
+ // merge delta into base
+ 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(hours[OPEN_INDEX].canConvert());
+ auto close = deltaHours.find(Hours::CLOSE);
+ if (close != deltaHours.end()) {
+ hours[CLOSE_INDEX] = close.value();
+ }
+ assert(hours[CLOSE_INDEX].canConvert());
+}
+
+void DomainMetadata::descriptorsChanged() {
+ // get descriptors
+ assert(_metadata[DESCRIPTORS].canConvert());
+ auto& state = *static_cast(_metadata[DESCRIPTORS].data());
+ auto settings = static_cast(parent())->_settingsManager.getSettingsMap();
+ auto descriptors = settings[DESCRIPTORS].toMap();
+
+ // copy simple descriptors (description/maturity)
+ state[Descriptors::DESCRIPTION] = descriptors[Descriptors::DESCRIPTION];
+ state[Descriptors::MATURITY] = descriptors[Descriptors::MATURITY];
+
+ // copy array descriptors (hosts/tags)
+ state[Descriptors::HOSTS] = descriptors[Descriptors::HOSTS].toList();
+ state[Descriptors::TAGS] = descriptors[Descriptors::TAGS].toList();
+
+ // parse capacity
const QString CAPACITY = "security.maximum_user_capacity";
const QVariant* capacityVariant = valueForKeyPath(settings, CAPACITY);
unsigned int capacity = capacityVariant ? capacityVariant->toUInt() : 0;
+ state[Descriptors::CAPACITY] = capacity;
- // TODO: Keep parity with ACL development.
- const QString RESTRICTION = "security.restricted_access";
- const QString RESTRICTION_OPEN = "open";
- // const QString RESTRICTION_HIFI = "hifi";
- const QString RESTRICTION_ACL = "acl";
- const QVariant* isRestrictedVariant = valueForKeyPath(settings, RESTRICTION);
- bool isRestricted = isRestrictedVariant ? isRestrictedVariant->toBool() : false;
- QString restriction = isRestricted ? RESTRICTION_ACL : RESTRICTION_OPEN;
-
- QVariantMap descriptors = settings[DESCRIPTORS].toMap();
- descriptors[DESCRIPTORS_CAPACITY] = capacity;
- descriptors[DESCRIPTORS_RESTRICTION] = restriction;
- _metadata[DESCRIPTORS] = descriptors;
+ // parse operating hours
+ const QString WEEKDAY_HOURS = "weekday_hours";
+ const QString WEEKEND_HOURS = "weekend_hours";
+ const QString UTC_OFFSET = "utc_offset";
+ assert(state[Descriptors::HOURS].canConvert());
+ auto& hours = *static_cast(state[Descriptors::HOURS].data());
+ parseHours(descriptors.take(WEEKDAY_HOURS), hours[Descriptors::Hours::WEEKDAY]);
+ parseHours(descriptors.take(WEEKEND_HOURS), hours[Descriptors::Hours::WEEKEND]);
+ hours[Descriptors::Hours::UTC_OFFSET] = descriptors.take(UTC_OFFSET);
#if DEV_BUILD || PR_BUILD
- qDebug() << "Domain metadata descriptors set:" << descriptors;
+ qDebug() << "Domain metadata descriptors set:" << QJsonObject::fromVariantMap(_metadata[DESCRIPTORS].toMap());
+#endif
+
+ sendDescriptors();
+}
+
+void DomainMetadata::securityChanged(bool send) {
+ // get descriptors
+ assert(_metadata[DESCRIPTORS].canConvert());
+ auto& state = *static_cast(_metadata[DESCRIPTORS].data());
+
+ const QString RESTRICTION_OPEN = "open";
+ const QString RESTRICTION_ANON = "anon";
+ const QString RESTRICTION_HIFI = "hifi";
+ const QString RESTRICTION_ACL = "acl";
+
+ QString restriction;
+
+ const auto& settingsManager = static_cast(parent())->_settingsManager;
+ bool hasAnonymousAccess =
+ settingsManager.getStandardPermissionsForName(NodePermissions::standardNameAnonymous).canConnectToDomain;
+ bool hasHifiAccess =
+ settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLoggedIn).canConnectToDomain;
+ if (hasAnonymousAccess) {
+ restriction = hasHifiAccess ? RESTRICTION_OPEN : RESTRICTION_ANON;
+ } else if (hasHifiAccess) {
+ restriction = RESTRICTION_HIFI;
+ } else {
+ restriction = RESTRICTION_ACL;
+ }
+
+ state[Descriptors::RESTRICTION] = restriction;
+
+#if DEV_BUILD || PR_BUILD
+ qDebug() << "Domain metadata restriction set:" << restriction;
+#endif
+
+ if (send) {
+ sendDescriptors();
+ }
+}
+
+void DomainMetadata::usersChanged() {
+ ++_tic;
+
+#if DEV_BUILD || PR_BUILD
+ qDebug() << "Domain metadata users change detected";
#endif
}
-void DomainMetadata::updateUsers() {
+void DomainMetadata::maybeUpdateUsers() {
+ if (_lastTic == _tic) {
+ return;
+ }
+ _lastTic = _tic;
+
static const QString DEFAULT_HOSTNAME = "*";
auto nodeList = DependencyManager::get();
@@ -112,21 +249,32 @@ void DomainMetadata::updateUsers() {
}
});
- QVariantMap users = {
- { USERS_NUM_TOTAL, numConnected },
- { USERS_NUM_ANON, numConnectedAnonymously },
- { USERS_HOSTNAMES, userHostnames }};
- _metadata[USERS] = users;
+ assert(_metadata[USERS].canConvert());
+ auto& users = *static_cast(_metadata[USERS].data());
+ users[Users::NUM_TOTAL] = numConnected;
+ users[Users::NUM_ANON] = numConnectedAnonymously;
+ users[Users::HOSTNAMES] = userHostnames;
#if DEV_BUILD || PR_BUILD
- qDebug() << "Domain metadata users updated:" << users;
+ qDebug() << "Domain metadata users set:" << QJsonObject::fromVariantMap(_metadata[USERS].toMap());
#endif
}
-void DomainMetadata::usersChanged() {
- ++_tic;
+void DomainMetadata::sendDescriptors() {
+ QString domainUpdateJSON = QString("{\"domain\":%1}").arg(QString(QJsonDocument(get(DESCRIPTORS)).toJson(QJsonDocument::Compact)));
+ const QUuid& domainID = DependencyManager::get()->getSessionUUID();
+ if (!domainID.isNull()) {
+ static const QString DOMAIN_UPDATE = "/api/v1/domains/%1";
+ QString path { DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)) };
+ DependencyManager::get()->sendRequest(path,
+ AccountManagerAuth::Required,
+ QNetworkAccessManager::PutOperation,
+ JSONCallbackParameters(),
+ domainUpdateJSON.toUtf8());
#if DEV_BUILD || PR_BUILD
- qDebug() << "Domain metadata users change detected";
+ qDebug() << "Domain metadata sent to" << path;
+ qDebug() << "Domain metadata update:" << domainUpdateJSON;
#endif
+ }
}
diff --git a/domain-server/src/DomainMetadata.h b/domain-server/src/DomainMetadata.h
index 7d58d43182..41f3a60832 100644
--- a/domain-server/src/DomainMetadata.h
+++ b/domain-server/src/DomainMetadata.h
@@ -19,46 +19,56 @@
class DomainMetadata : public QObject {
Q_OBJECT
+public:
+ using Tic = uint32_t;
+
static const QString USERS;
- static const QString USERS_NUM_TOTAL;
- static const QString USERS_NUM_ANON;
- static const QString USERS_HOSTNAMES;
+ class Users {
+ public:
+ static const QString NUM_TOTAL;
+ static const QString NUM_ANON;
+ static const QString HOSTNAMES;
+ };
static const QString DESCRIPTORS;
- static const QString DESCRIPTORS_DESCRIPTION;
- static const QString DESCRIPTORS_CAPACITY;
- static const QString DESCRIPTORS_HOURS;
- static const QString DESCRIPTORS_RESTRICTION;
- static const QString DESCRIPTORS_MATURITY;
- static const QString DESCRIPTORS_HOSTS;
- static const QString DESCRIPTORS_TAGS;
- static const QString DESCRIPTORS_IMG;
- static const QString DESCRIPTORS_IMG_SRC;
- static const QString DESCRIPTORS_IMG_TYPE;
- static const QString DESCRIPTORS_IMG_SIZE;
- static const QString DESCRIPTORS_IMG_UPDATED_AT;
+ class Descriptors {
+ public:
+ static const QString DESCRIPTION;
+ static const QString CAPACITY;
+ static const QString RESTRICTION;
+ static const QString MATURITY;
+ static const QString HOSTS;
+ static const QString TAGS;
+ static const QString HOURS;
+ class Hours {
+ public:
+ static const QString WEEKDAY;
+ static const QString WEEKEND;
+ static const QString UTC_OFFSET;
+ static const QString OPEN;
+ static const QString CLOSE;
+ };
+ };
-public:
- DomainMetadata();
+ DomainMetadata(QObject* domainServer);
+ DomainMetadata() = delete;
- // Returns the last set metadata
- // If connected users have changed, metadata may need to be updated
- // this should be checked by storing tic = getTic() between calls
- // and testing it for equality before the next get (tic == getTic())
- QJsonObject get() { return QJsonObject::fromVariantMap(_metadata); }
- QJsonObject getUsers() { return QJsonObject::fromVariantMap(_metadata[USERS].toMap()); }
- QJsonObject getDescriptors() { return QJsonObject::fromVariantMap(_metadata[DESCRIPTORS].toMap()); }
-
- uint32_t getTic() { return _tic; }
-
- void setDescriptors(QVariantMap& settings);
- void updateUsers();
+ // Get cached metadata
+ QJsonObject get();
+ QJsonObject get(const QString& group);
public slots:
+ void descriptorsChanged();
+ void securityChanged(bool send);
+ void securityChanged() { securityChanged(true); }
void usersChanged();
protected:
+ void maybeUpdateUsers();
+ void sendDescriptors();
+
QVariantMap _metadata;
+ uint32_t _lastTic{ (uint32_t)-1 };
uint32_t _tic{ 0 };
};
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 7c596bb187..223cab61da 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -94,10 +94,6 @@ DomainServer::DomainServer(int argc, char* argv[]) :
qRegisterMetaType("DomainServerWebSessionData");
qRegisterMetaTypeStreamOperators("DomainServerWebSessionData");
- // update the metadata when a user (dis)connects
- connect(this, &DomainServer::userConnected, &_metadata, &DomainMetadata::usersChanged);
- connect(this, &DomainServer::userDisconnected, &_metadata, &DomainMetadata::usersChanged);
-
// make sure we hear about newly connected nodes from our gatekeeper
connect(&_gatekeeper, &DomainGatekeeper::connectedNode, this, &DomainServer::handleConnectedNode);
@@ -124,8 +120,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
optionallyGetTemporaryName(args);
}
- // update the metadata with current descriptors
- _metadata.setDescriptors(_settingsManager.getSettingsMap());
+ _metadata = new DomainMetadata(this);
}
DomainServer::~DomainServer() {
@@ -1101,14 +1096,11 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) {
NodePermissions anonymousPermissions = _settingsManager.getPermissionsForName(NodePermissions::standardNameAnonymous);
domainObject[RESTRICTED_ACCESS_FLAG] = !anonymousPermissions.canConnectToDomain;
- // Add the metadata to the heartbeat
- static const QString DOMAIN_HEARTBEAT_KEY = "heartbeat";
- auto tic = _metadata.getTic();
- if (_metadataTic != tic) {
- _metadataTic = tic;
- _metadata.updateUsers();
+ if (_metadata) {
+ // Add the metadata to the heartbeat
+ static const QString DOMAIN_HEARTBEAT_KEY = "heartbeat";
+ domainObject[DOMAIN_HEARTBEAT_KEY] = _metadata->get(DomainMetadata::USERS);
}
- domainObject[DOMAIN_HEARTBEAT_KEY] = _metadata.getUsers();
QString domainUpdateJSON = QString("{\"domain\":%1}").arg(QString(QJsonDocument(domainObject).toJson(QJsonDocument::Compact)));
diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h
index bdcc36c1ac..c742dbc9b3 100644
--- a/domain-server/src/DomainServer.h
+++ b/domain-server/src/DomainServer.h
@@ -172,13 +172,12 @@ private:
DomainServerSettingsManager _settingsManager;
- DomainMetadata _metadata;
- uint32_t _metadataTic{ 0 };
-
HifiSockAddr _iceServerSocket;
std::unique_ptr _iceServerHeartbeatPacket;
- QTimer* _iceHeartbeatTimer { nullptr }; // this looks like it dangles when created but it's parented to the DomainServer
+ // These will be parented to this, they are not dangling
+ DomainMetadata* _metadata { nullptr };
+ QTimer* _iceHeartbeatTimer { nullptr };
QList _iceServerAddresses;
QSet _failedIceServerAddresses;
@@ -190,6 +189,7 @@ private:
bool _hasAccessToken { false };
friend class DomainGatekeeper;
+ friend class DomainMetadata;
};
diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp
index 5790eb9178..543e61f485 100644
--- a/domain-server/src/DomainServerSettingsManager.cpp
+++ b/domain-server/src/DomainServerSettingsManager.cpp
@@ -21,6 +21,8 @@
#include
#include
+#include
+
#include
#include
#include
@@ -69,7 +71,7 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
}
static const QString MISSING_SETTINGS_DESC_MSG =
- QString("Did not find settings decription in JSON at %1 - Unable to continue. domain-server will quit.\n%2 at %3")
+ QString("Did not find settings description in JSON at %1 - Unable to continue. domain-server will quit.\n%2 at %3")
.arg(SETTINGS_DESCRIPTION_RELATIVE_PATH).arg(parseError.errorString()).arg(parseError.offset);
static const int MISSING_SETTINGS_DESC_ERROR_CODE = 6;
@@ -241,7 +243,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
}
QList> permissionsSets;
- permissionsSets << _standardAgentPermissions << _agentPermissions;
+ permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get();
foreach (auto permissionsSet, permissionsSets) {
foreach (QString userName, permissionsSet.keys()) {
if (onlyEditorsAreRezzers) {
@@ -258,6 +260,27 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
_standardAgentPermissions.clear();
_agentPermissions.clear();
}
+
+ if (oldVersion < 1.5) {
+ // This was prior to operating hours, so add default hours
+ static const QString WEEKDAY_HOURS{ "descriptors.weekday_hours" };
+ static const QString WEEKEND_HOURS{ "descriptors.weekend_hours" };
+ static const QString UTC_OFFSET{ "descriptors.utc_offset" };
+
+ QVariant* weekdayHours = valueForKeyPath(_configMap.getUserConfig(), WEEKDAY_HOURS, true);
+ QVariant* weekendHours = valueForKeyPath(_configMap.getUserConfig(), WEEKEND_HOURS, true);
+ QVariant* utcOffset = valueForKeyPath(_configMap.getUserConfig(), UTC_OFFSET, true);
+
+ *weekdayHours = QVariantList { QVariantMap{ { "open", QVariant("00:00") }, { "close", QVariant("23:59") } } };
+ *weekendHours = QVariantList { QVariantMap{ { "open", QVariant("00:00") }, { "close", QVariant("23:59") } } };
+ *utcOffset = QVariant(QTimeZone::systemTimeZone().offsetFromUtc(QDateTime::currentDateTime()) / (float)3600);
+
+ // write the new settings to file
+ persistToFile();
+
+ // reload the master and user config so the merged config is correct
+ _configMap.loadMasterAndUserConfig(_argumentList);
+ }
}
unpackPermissions();
@@ -267,7 +290,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
}
void DomainServerSettingsManager::packPermissionsForMap(QString mapName,
- QHash agentPermissions,
+ NodePermissionsMap& agentPermissions,
QString keyPath) {
QVariant* security = valueForKeyPath(_configMap.getUserConfig(), "security");
if (!security || !security->canConvert(QMetaType::QVariantMap)) {
@@ -378,7 +401,7 @@ void DomainServerSettingsManager::unpackPermissions() {
#ifdef WANT_DEBUG
qDebug() << "--------------- permissions ---------------------";
QList> permissionsSets;
- permissionsSets << _standardAgentPermissions << _agentPermissions;
+ permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get();
foreach (auto permissionSet, permissionsSets) {
QHashIterator i(permissionSet);
while (i.hasNext()) {
diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h
index 446e9a2eed..ec1d3b637d 100644
--- a/domain-server/src/DomainServerSettingsManager.h
+++ b/domain-server/src/DomainServerSettingsManager.h
@@ -72,11 +72,11 @@ private:
friend class DomainServer;
- void packPermissionsForMap(QString mapName, QHash agentPermissions, QString keyPath);
+ void packPermissionsForMap(QString mapName, NodePermissionsMap& agentPermissions, QString keyPath);
void packPermissions();
void unpackPermissions();
- QHash _standardAgentPermissions; // anonymous, logged-in, localhost
- QHash _agentPermissions; // specific account-names
+ NodePermissionsMap _standardAgentPermissions; // anonymous, logged-in, localhost
+ NodePermissionsMap _agentPermissions; // specific account-names
};
#endif // hifi_DomainServerSettingsManager_h
diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt
index ae84705da3..cf5a2b60ad 100644
--- a/interface/CMakeLists.txt
+++ b/interface/CMakeLists.txt
@@ -139,7 +139,7 @@ link_hifi_libraries(shared octree gpu gl gpu-gl procedural model render
recording fbx networking model-networking entities avatars
audio audio-client animation script-engine physics
render-utils entities-renderer ui auto-updater
- controllers plugins display-plugins input-plugins steamworks-wrapper)
+ controllers plugins ui-plugins display-plugins input-plugins steamworks-wrapper)
# include the binary directory of render-utils for shader includes
target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries/render-utils")
diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml
index e269a63cde..9f10cfc64a 100644
--- a/interface/resources/qml/desktop/Desktop.qml
+++ b/interface/resources/qml/desktop/Desktop.qml
@@ -65,7 +65,7 @@ FocusScope {
var oldChildren = expectedChildren;
var newChildren = d.getRepositionChildren();
- if (oldRecommendedRect != Qt.rect(0,0,0,0)
+ if (oldRecommendedRect != Qt.rect(0,0,0,0) && oldRecommendedRect != Qt.rect(0,0,1,1)
&& (oldRecommendedRect != newRecommendedRect
|| oldChildren != newChildren)
) {
@@ -297,6 +297,9 @@ FocusScope {
onPinnedChanged: {
if (pinned) {
+ nullFocus.focus = true;
+ nullFocus.forceActiveFocus();
+
// recalculate our non-pinned children
hiddenChildren = d.findMatchingChildren(desktop, function(child){
return !d.isTopLevelWindow(child) && child.visible && !child.pinned;
@@ -478,6 +481,8 @@ FocusScope {
FocusHack { id: focusHack; }
+ FocusScope { id: nullFocus; }
+
Rectangle {
id: focusDebugger;
objectName: "focusDebugger"
diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml
index fc10bd4f68..5372028da5 100644
--- a/interface/resources/qml/dialogs/FileDialog.qml
+++ b/interface/resources/qml/dialogs/FileDialog.qml
@@ -614,12 +614,6 @@ ModalWindow {
readOnly: !root.saveDialog
activeFocusOnTab: !readOnly
onActiveFocusChanged: if (activeFocus) { selectAll(); }
- onTextChanged: {
- if (root.saveDialog && text !== "") {
- fileTableView.selection.clear();
- fileTableView.currentRow = -1;
- }
- }
onAccepted: okAction.trigger();
}
diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml
index c14d55cb00..169542c0f0 100644
--- a/interface/resources/qml/hifi/Desktop.qml
+++ b/interface/resources/qml/hifi/Desktop.qml
@@ -3,12 +3,12 @@ import QtQuick.Controls 1.4
import QtWebEngine 1.1;
import Qt.labs.settings 1.0
-import "../desktop"
+import "../desktop" as OriginalDesktop
import ".."
import "."
import "./toolbars"
-Desktop {
+OriginalDesktop.Desktop {
id: desktop
MouseArea {
@@ -54,12 +54,13 @@ Desktop {
WebEngine.settings.localContentCanAccessRemoteUrls = true;
var sysToolbar = desktop.getToolbar("com.highfidelity.interface.toolbar.system");
- //toolbars[sysToolbar.objectName] = sysToolbar
var toggleHudButton = sysToolbar.addButton({
+ objectName: "hudToggle",
imageURL: "../../../icons/hud-01.svg",
visible: true,
-
+ pinned: true,
});
+
toggleHudButton.yOffset = Qt.binding(function(){
return desktop.pinned ? 50 : 0
});
diff --git a/interface/resources/qml/hifi/toolbars/Toolbar.qml b/interface/resources/qml/hifi/toolbars/Toolbar.qml
index 35c816569b..75c06e4199 100644
--- a/interface/resources/qml/hifi/toolbars/Toolbar.qml
+++ b/interface/resources/qml/hifi/toolbars/Toolbar.qml
@@ -7,14 +7,16 @@ import "."
Window {
id: window
- frame: ToolFrame { }
+ frame: ToolFrame {
+ horizontalSpacers: horizontal
+ verticalSpacers: !horizontal
+ }
hideBackground: true
resizable: false
destroyOnCloseButton: false
destroyOnHidden: false
closable: false
shown: true
- pinned: true
width: content.width
height: content.height
visible: true
@@ -32,54 +34,77 @@ Window {
}
onHorizontalChanged: {
- var oldParent = horizontal ? column : row;
var newParent = horizontal ? row : column;
- var move = [];
-
- var i;
- for (i in oldParent.children) {
- var child = oldParent.children[i];
- if (child.spacer) {
- continue;
- }
- move.push(oldParent.children[i]);
- }
- for (i in move) {
- move[i].parent = newParent;
+ for (var i in buttons) {
+ var child = buttons[i];
+ child.parent = newParent;
if (horizontal) {
- move[i].y = 0
+ child.y = 0
} else {
- move[i].x = 0
+ child.x = 0
}
}
- fixSpacers();
}
Item {
id: content
implicitHeight: horizontal ? row.height : column.height
implicitWidth: horizontal ? row.width : column.width
+
Row {
id: row
spacing: 6
- visible: window.horizontal
- Rectangle{ readonly property bool spacer: true; id: rowSpacer1; width: 1; height: row.height }
- Rectangle{ readonly property bool spacer: true; id: rowSpacer2; width: 1; height: row.height }
- Rectangle{ readonly property bool spacer: true; id: rowSpacer3; width: 1; height: row.height }
- Rectangle{ readonly property bool spacer: true; id: rowSpacer4; width: 1; height: row.height }
}
Column {
id: column
spacing: 6
- visible: !window.horizontal
- Rectangle{ readonly property bool spacer: true; id: colSpacer1; width: column.width; height: 1 }
- Rectangle{ readonly property bool spacer: true; id: colSpacer2; width: column.width; height: 1 }
- Rectangle{ readonly property bool spacer: true; id: colSpacer3; width: column.width; height: 1 }
- Rectangle{ readonly property bool spacer: true; id: colSpacer4; width: column.width; height: 1 }
}
Component { id: toolbarButtonBuilder; ToolbarButton { } }
+
+ Connections {
+ target: desktop
+ onPinnedChanged: {
+ if (!window.pinned) {
+ return;
+ }
+ var newPinned = desktop.pinned;
+ for (var i in buttons) {
+ var child = buttons[i];
+ if (desktop.pinned) {
+ if (!child.pinned) {
+ child.visible = false;
+ }
+ } else {
+ child.visible = true;
+ }
+ }
+ }
+ }
+ }
+
+
+ function findButtonIndex(name) {
+ if (!name) {
+ return -1;
+ }
+
+ for (var i in buttons) {
+ var child = buttons[i];
+ if (child.objectName === name) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ function findButton(name) {
+ var index = findButtonIndex(name);
+ if (index < 0) {
+ return;
+ }
+ return buttons[index];
}
function addButton(properties) {
@@ -88,30 +113,39 @@ Window {
// If a name is specified, then check if there's an existing button with that name
// and return it if so. This will allow multiple clients to listen to a single button,
// and allow scripts to be idempotent so they don't duplicate buttons if they're reloaded
- if (properties.objectName) {
- for (var i in buttons) {
- var child = buttons[i];
- if (child.objectName === properties.objectName) {
- return child;
- }
- }
+ var result = findButton(properties.objectName);
+ if (result) {
+ return result;
}
-
properties.toolbar = this;
- var result = toolbarButtonBuilder.createObject(container, properties);
+ properties.opacity = 0;
+ result = toolbarButtonBuilder.createObject(container, properties);
buttons.push(result);
- fixSpacers();
+ result.opacity = 1;
+ updatePinned();
return result;
}
- function fixSpacers() {
- colSpacer3.parent = null
- colSpacer4.parent = null
- rowSpacer3.parent = null
- rowSpacer4.parent = null
- colSpacer3.parent = column
- colSpacer4.parent = column
- rowSpacer3.parent = row
- rowSpacer4.parent = row
+ function removeButton(name) {
+ var index = findButtonIndex(name);
+ if (index < -1) {
+ console.warn("Tried to remove non-existent button " + name);
+ return;
+ }
+ buttons[index].destroy();
+ buttons.splice(index, 1);
+ updatePinned();
+ }
+
+ function updatePinned() {
+ var newPinned = false;
+ for (var i in buttons) {
+ var child = buttons[i];
+ if (child.pinned) {
+ newPinned = true;
+ break;
+ }
+ }
+ pinned = newPinned;
}
}
diff --git a/interface/resources/qml/hifi/toolbars/ToolbarButton.qml b/interface/resources/qml/hifi/toolbars/ToolbarButton.qml
index a8514689e8..a3be4533d2 100644
--- a/interface/resources/qml/hifi/toolbars/ToolbarButton.qml
+++ b/interface/resources/qml/hifi/toolbars/ToolbarButton.qml
@@ -7,11 +7,40 @@ Item {
property alias alpha: button.opacity
property var subImage;
property int yOffset: 0
+ property int buttonState: 0
property var toolbar;
property real size: 50 // toolbar ? toolbar.buttonSize : 50
width: size; height: size
+ property bool pinned: false
clip: true
+ Behavior on opacity {
+ NumberAnimation {
+ duration: 150
+ easing.type: Easing.InOutCubic
+ }
+ }
+
+ property alias fadeTargetProperty: button.opacity
+
+ onFadeTargetPropertyChanged: {
+ visible = (fadeTargetProperty !== 0.0);
+ }
+
+ onVisibleChanged: {
+ if ((!visible && fadeTargetProperty != 0.0) || (visible && fadeTargetProperty == 0.0)) {
+ var target = visible;
+ visible = !visible;
+ fadeTargetProperty = target ? 1.0 : 0.0;
+ return;
+ }
+ }
+
+
+ onButtonStateChanged: {
+ yOffset = size * buttonState
+ }
+
Component.onCompleted: {
if (subImage) {
if (subImage.y) {
@@ -30,10 +59,7 @@ Item {
MouseArea {
anchors.fill: parent
- onClicked: {
- console.log("Clicked on button " + image.source + " named " + button.objectName)
- button.clicked();
- }
+ onClicked: button.clicked();
}
}
diff --git a/interface/resources/qml/windows/Frame.qml b/interface/resources/qml/windows/Frame.qml
index bc8ecc35ec..88d8c3ad41 100644
--- a/interface/resources/qml/windows/Frame.qml
+++ b/interface/resources/qml/windows/Frame.qml
@@ -59,7 +59,7 @@ Item {
height: 1.66 * window.height
x: (window.width - width) / 2
y: window.height / 2 - 0.375 * height
- visible: gradientsSupported && window && window.focus && pane.visible
+ visible: gradientsSupported && window && window.focus && window.content.visible
gradient: Gradient {
// GradientStop position 0.5 is at full circumference of circle that fits inside the square.
GradientStop { position: 0.0; color: "#ff000000" } // black, 100% opacity
diff --git a/interface/resources/qml/windows/ToolFrame.qml b/interface/resources/qml/windows/ToolFrame.qml
index ac1093092e..eff5fc0377 100644
--- a/interface/resources/qml/windows/ToolFrame.qml
+++ b/interface/resources/qml/windows/ToolFrame.qml
@@ -16,20 +16,67 @@ import "../styles-uit"
Frame {
HifiConstants { id: hifi }
+ property bool horizontalSpacers: false
+ property bool verticalSpacers: false
Rectangle {
// Dialog frame
id: frameContent
readonly property int frameMargin: 6
- readonly property int frameMarginLeft: frameMargin
- readonly property int frameMarginRight: frameMargin
- readonly property int frameMarginTop: frameMargin
- readonly property int frameMarginBottom: frameMargin
+ readonly property int frameMarginLeft: frameMargin + (horizontalSpacers ? 12 : 0)
+ readonly property int frameMarginRight: frameMargin + (horizontalSpacers ? 12 : 0)
+ readonly property int frameMarginTop: frameMargin + (verticalSpacers ? 12 : 0)
+ readonly property int frameMarginBottom: frameMargin + (verticalSpacers ? 12 : 0)
+
+ Rectangle {
+ visible: horizontalSpacers
+ anchors.left: parent.left
+ anchors.leftMargin: 6
+ anchors.verticalCenter: parent.verticalCenter
+ width: 8
+ height: window.height
+ color: "gray";
+ radius: 4
+ }
+
+ Rectangle {
+ visible: horizontalSpacers
+ anchors.right: parent.right
+ anchors.rightMargin: 6
+ anchors.verticalCenter: parent.verticalCenter
+ width: 8
+ height: window.height
+ color: "gray";
+ radius: 4
+ }
+
+ Rectangle {
+ visible: verticalSpacers
+ anchors.top: parent.top
+ anchors.topMargin: 6
+ anchors.horizontalCenter: parent.horizontalCenter
+ height: 8
+ width: window.width
+ color: "gray";
+ radius: 4
+ }
+
+ Rectangle {
+ visible: verticalSpacers
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 6
+ anchors.horizontalCenter: parent.horizontalCenter
+ height: 8
+ width: window.width
+ color: "gray";
+ radius: 4
+ }
+
anchors {
- topMargin: -frameMargin
- leftMargin: -frameMargin
- rightMargin: -frameMargin
- bottomMargin: -frameMargin
+ leftMargin: -frameMarginLeft
+ rightMargin: -frameMarginRight
+ topMargin: -frameMarginTop
+ bottomMargin: -frameMarginBottom
}
anchors.fill: parent
color: hifi.colors.baseGrayHighlight40
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 6dc575b572..37c3b361bf 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -68,6 +68,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -83,7 +84,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -119,7 +119,6 @@
#include "InterfaceLogging.h"
#include "LODManager.h"
#include "ModelPackager.h"
-#include "PluginContainerProxy.h"
#include "scripting/AccountScriptingInterface.h"
#include "scripting/AssetMappingsScriptingInterface.h"
#include "scripting/AudioDeviceScriptingInterface.h"
@@ -152,6 +151,8 @@
#include "InterfaceParentFinder.h"
#include "FrameTimingsScriptingInterface.h"
+#include
+#include
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
// FIXME seems to be broken.
@@ -439,7 +440,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set();
DependencyManager::set();
DependencyManager::set();
-
+ DependencyManager::set();
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
DependencyManager::set();
@@ -466,7 +467,6 @@ bool setupEssentials(int& argc, char** argv) {
// continuing to overburden Application.cpp
Cube3DOverlay* _keyboardFocusHighlight{ nullptr };
int _keyboardFocusHighlightID{ -1 };
-PluginContainer* _pluginContainer;
// FIXME hack access to the internal share context for the Chromium helper
@@ -506,6 +506,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_maxOctreePPS(maxOctreePacketsPerSecond.get()),
_lastFaceTrackerUpdate(0)
{
+
+
+ PluginContainer* pluginContainer = dynamic_cast(this); // set the container for any plugins that care
+ PluginManager::getInstance()->setContainer(pluginContainer);
+
// FIXME this may be excessively conservative. On the other hand
// maybe I'm used to having an 8-core machine
// Perhaps find the ideal thread count and subtract 2 or 3
@@ -523,7 +528,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_entityClipboard->createRootElement();
- _pluginContainer = new PluginContainerProxy();
#ifdef Q_OS_WIN
installNativeEventFilter(&MyNativeEventFilter::getInstance());
#endif
@@ -674,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();
// use our MyAvatar position and quat for address manager path
@@ -767,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(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());
@@ -1062,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(memInfo.totalMemoryBytes);
+ properties["system_memory_used"] = static_cast(memInfo.usedMemoryBytes);
+ properties["process_memory_used"] = static_cast(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();
+ 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();
+ 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();
+ 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().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().data(), &DialogsManager::addressBarToggled, this, onAddressBarToggled);
+
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
@@ -2037,9 +2153,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
case Qt::Key_Return:
if (isOption) {
if (_window->isFullScreen()) {
- _pluginContainer->unsetFullscreen();
+ unsetFullscreen();
} else {
- _pluginContainer->setFullscreen(nullptr);
+ setFullscreen(nullptr);
}
} else {
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
@@ -2953,7 +3069,6 @@ void Application::loadSettings() {
//DependencyManager::get()->setAutomaticLODAdjust(false);
Menu::getInstance()->loadSettings();
-
// If there is a preferred plugin, we probably messed it up with the menu settings, so fix it.
auto pluginManager = PluginManager::getInstance();
auto plugins = pluginManager->getPreferredDisplayPlugins();
@@ -4578,6 +4693,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get().data());
scriptEngine->registerGlobalObject("Reticle", getApplicationCompositor().getReticleInterface());
+
+ scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get().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();
// Make the switch atomic from the perspective of other threads
@@ -5310,3 +5432,49 @@ void Application::showDesktop() {
CompositorHelper& Application::getApplicationCompositor() const {
return *DependencyManager::get();
}
+
+
+// virtual functions required for PluginContainer
+ui::Menu* Application::getPrimaryMenu() {
+ auto appMenu = _window->menuBar();
+ auto uiMenu = dynamic_cast(appMenu);
+ return uiMenu;
+}
+
+void Application::showDisplayPluginsTools(bool show) {
+ DependencyManager::get()->hmdTools(show);
+}
+
+GLWidget* Application::getPrimaryWidget() {
+ return _glWidget;
+}
+
+MainWindow* Application::getPrimaryWindow() {
+ return getWindow();
+}
+
+QOpenGLContext* Application::getPrimaryContext() {
+ return _glWidget->context()->contextHandle();
+}
+
+bool Application::makeRenderingContextCurrent() {
+ return _offscreenContext->makeCurrent();
+}
+
+void Application::releaseSceneTexture(const gpu::TexturePointer& texture) {
+ Q_ASSERT(QThread::currentThread() == thread());
+ auto& framebufferMap = _lockedFramebufferMap;
+ Q_ASSERT(framebufferMap.contains(texture));
+ auto framebufferPointer = framebufferMap[texture];
+ framebufferMap.remove(texture);
+ auto framebufferCache = DependencyManager::get();
+ framebufferCache->releaseFramebuffer(framebufferPointer);
+}
+
+void Application::releaseOverlayTexture(const gpu::TexturePointer& texture) {
+ _applicationOverlay.releaseOverlay(texture);
+}
+
+bool Application::isForeground() const {
+ return _isForeground && !_window->isMinimized();
+}
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 6b6148be32..5beaa5b455 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -34,6 +34,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -86,14 +87,32 @@ class Application;
#endif
#define qApp (static_cast(QCoreApplication::instance()))
-class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface, public AbstractUriHandler {
+class Application : public QApplication,
+ public AbstractViewStateInterface,
+ public AbstractScriptingServicesInterface,
+ public AbstractUriHandler,
+ public PluginContainer {
Q_OBJECT
// TODO? Get rid of those
friend class OctreePacketProcessor;
- friend class PluginContainerProxy;
public:
+
+ // virtual functions required for PluginContainer
+ virtual ui::Menu* getPrimaryMenu() override;
+ virtual void requestReset() override { resetSensors(true); }
+ virtual void showDisplayPluginsTools(bool show) override;
+ virtual GLWidget* getPrimaryWidget() override;
+ virtual MainWindow* getPrimaryWindow() override;
+ virtual QOpenGLContext* getPrimaryContext() override;
+ virtual bool makeRenderingContextCurrent() override;
+ virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override;
+ virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override;
+ virtual bool isForeground() const override;
+
+ virtual DisplayPluginPointer getActiveDisplayPlugin() const override;
+
enum Event {
Present = DisplayPlugin::Present,
Paint = Present + 1,
@@ -163,7 +182,6 @@ public:
Overlays& getOverlays() { return _overlays; }
- bool isForeground() const { return _isForeground; }
size_t getFrameCount() const { return _frameCount; }
float getFps() const { return _frameCounter.rate(); }
@@ -185,8 +203,6 @@ public:
void setActiveDisplayPlugin(const QString& pluginName);
- DisplayPluginPointer getActiveDisplayPlugin() const;
-
FileLogger* getLogger() const { return _logger; }
glm::vec2 getViewportDimensions() const;
@@ -195,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
diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp
index 24256fdf39..c4d985419e 100644
--- a/interface/src/DiscoverabilityManager.cpp
+++ b/interface/src/DiscoverabilityManager.cpp
@@ -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->setSessionID(_sessionID);
+ accountManager->setSessionID(sessionID);
}
}
diff --git a/interface/src/DiscoverabilityManager.h b/interface/src/DiscoverabilityManager.h
index 9a1fa7b39c..196b0cdf81 100644
--- a/interface/src/DiscoverabilityManager.h
+++ b/interface/src/DiscoverabilityManager.h
@@ -49,7 +49,6 @@ private:
DiscoverabilityManager();
Setting::Handle _mode;
- QString _sessionID;
QJsonObject _lastLocationObject;
};
diff --git a/interface/src/PluginContainerProxy.cpp b/interface/src/PluginContainerProxy.cpp
deleted file mode 100644
index b651a1520d..0000000000
--- a/interface/src/PluginContainerProxy.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-#include "PluginContainerProxy.h"
-
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-
-#include "Application.h"
-#include "MainWindow.h"
-#include "GLCanvas.h"
-#include "ui/DialogsManager.h"
-
-#include
-#include
-
-PluginContainerProxy::PluginContainerProxy() {
-}
-
-PluginContainerProxy::~PluginContainerProxy() {
-}
-
-ui::Menu* PluginContainerProxy::getPrimaryMenu() {
- auto appMenu = qApp->_window->menuBar();
- auto uiMenu = dynamic_cast(appMenu);
- return uiMenu;
-}
-
-bool PluginContainerProxy::isForeground() {
- return qApp->isForeground() && !qApp->getWindow()->isMinimized();
-}
-
-void PluginContainerProxy::requestReset() {
- // We could signal qApp to sequence this, but it turns out that requestReset is only used from within the main thread anyway.
- qApp->resetSensors(true);
-}
-
-void PluginContainerProxy::showDisplayPluginsTools(bool show) {
- DependencyManager::get()->hmdTools(show);
-}
-
-GLWidget* PluginContainerProxy::getPrimaryWidget() {
- return qApp->_glWidget;
-}
-
-MainWindow* PluginContainerProxy::getPrimaryWindow() {
- return qApp->getWindow();
-}
-
-QOpenGLContext* PluginContainerProxy::getPrimaryContext() {
- return qApp->_glWidget->context()->contextHandle();
-}
-
-const DisplayPluginPointer PluginContainerProxy::getActiveDisplayPlugin() const {
- return qApp->getActiveDisplayPlugin();
-}
-
-bool PluginContainerProxy::makeRenderingContextCurrent() {
- return qApp->_offscreenContext->makeCurrent();
-}
-
-void PluginContainerProxy::releaseSceneTexture(const gpu::TexturePointer& texture) {
- Q_ASSERT(QThread::currentThread() == qApp->thread());
- auto& framebufferMap = qApp->_lockedFramebufferMap;
- Q_ASSERT(framebufferMap.contains(texture));
- auto framebufferPointer = framebufferMap[texture];
- framebufferMap.remove(texture);
- auto framebufferCache = DependencyManager::get();
- framebufferCache->releaseFramebuffer(framebufferPointer);
-}
-
-void PluginContainerProxy::releaseOverlayTexture(const gpu::TexturePointer& texture) {
- qApp->_applicationOverlay.releaseOverlay(texture);
-}
-
diff --git a/interface/src/PluginContainerProxy.h b/interface/src/PluginContainerProxy.h
deleted file mode 100644
index a04a1b2977..0000000000
--- a/interface/src/PluginContainerProxy.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-#ifndef hifi_PluginContainerProxy_h
-#define hifi_PluginContainerProxy_h
-
-#include
-#include
-
-#include
-#include
-
-class QActionGroup;
-
-class PluginContainerProxy : public QObject, PluginContainer {
- Q_OBJECT
- PluginContainerProxy();
- virtual ~PluginContainerProxy();
- virtual void showDisplayPluginsTools(bool show = true) override;
- virtual void requestReset() override;
- virtual bool makeRenderingContextCurrent() override;
- virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override;
- virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override;
- virtual GLWidget* getPrimaryWidget() override;
- virtual MainWindow* getPrimaryWindow() override;
- virtual ui::Menu* getPrimaryMenu() override;
- virtual QOpenGLContext* getPrimaryContext() override;
- virtual bool isForeground() override;
- virtual const DisplayPluginPointer getActiveDisplayPlugin() const override;
-
- friend class Application;
-
-};
-
-#endif
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 5d1efca902..6a69ee9a9a 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -722,7 +722,7 @@ void MyAvatar::saveData() {
settings.setValue("displayName", _displayName);
settings.setValue("collisionSoundURL", _collisionSoundURL);
settings.setValue("useSnapTurn", _useSnapTurn);
- settings.setValue("clearOverlayWhenDriving", _clearOverlayWhenDriving);
+ settings.setValue("clearOverlayWhenMoving", _clearOverlayWhenMoving);
settings.endGroup();
}
@@ -842,7 +842,7 @@ void MyAvatar::loadData() {
setDisplayName(settings.value("displayName").toString());
setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString());
setSnapTurn(settings.value("useSnapTurn", _useSnapTurn).toBool());
- setClearOverlayWhenDriving(settings.value("clearOverlayWhenDriving", _clearOverlayWhenDriving).toBool());
+ setClearOverlayWhenMoving(settings.value("clearOverlayWhenMoving", _clearOverlayWhenMoving).toBool());
settings.endGroup();
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index 6fa6e1fd19..96fa999de5 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -159,8 +159,8 @@ public:
Q_INVOKABLE bool getSnapTurn() const { return _useSnapTurn; }
Q_INVOKABLE void setSnapTurn(bool on) { _useSnapTurn = on; }
- Q_INVOKABLE bool getClearOverlayWhenDriving() const { return _clearOverlayWhenDriving; }
- Q_INVOKABLE void setClearOverlayWhenDriving(bool on) { _clearOverlayWhenDriving = on; }
+ Q_INVOKABLE bool getClearOverlayWhenMoving() const { return _clearOverlayWhenMoving; }
+ Q_INVOKABLE void setClearOverlayWhenMoving(bool on) { _clearOverlayWhenMoving = on; }
Q_INVOKABLE void setHMDLeanRecenterEnabled(bool value) { _hmdLeanRecenterEnabled = value; }
Q_INVOKABLE bool getHMDLeanRecenterEnabled() const { return _hmdLeanRecenterEnabled; }
@@ -405,7 +405,7 @@ private:
QString _fullAvatarModelName;
QUrl _animGraphUrl {""};
bool _useSnapTurn { true };
- bool _clearOverlayWhenDriving { false };
+ bool _clearOverlayWhenMoving { true };
// cache of the current HMD sensor position and orientation
// in sensor space.
diff --git a/interface/src/scripting/ToolbarScriptingInterface.cpp b/interface/src/scripting/ToolbarScriptingInterface.cpp
index fd96b6a809..82332b3187 100644
--- a/interface/src/scripting/ToolbarScriptingInterface.cpp
+++ b/interface/src/scripting/ToolbarScriptingInterface.cpp
@@ -15,15 +15,6 @@ class QmlWrapper : public QObject {
public:
QmlWrapper(QObject* qmlObject, QObject* parent = nullptr)
: QObject(parent), _qmlObject(qmlObject) {
-
- const QMetaObject *metaobject = qmlObject->metaObject();
- int count = metaobject->propertyCount();
- qDebug() << "Scanning properties for " << qmlObject;
- for (int i = 0; i < count; ++i) {
- QMetaProperty metaproperty = metaobject->property(i);
- const char *name = metaproperty.name();
- qDebug() << "Property " << name;
- }
}
Q_INVOKABLE void writeProperty(QString propertyName, QVariant propertyValue) {
diff --git a/interface/src/scripting/ToolbarScriptingInterface.h b/interface/src/scripting/ToolbarScriptingInterface.h
index d3706cb6e1..9379284e55 100644
--- a/interface/src/scripting/ToolbarScriptingInterface.h
+++ b/interface/src/scripting/ToolbarScriptingInterface.h
@@ -20,7 +20,7 @@ class ToolbarProxy;
class ToolbarScriptingInterface : public QObject, public Dependency {
Q_OBJECT
public:
- Q_INVOKABLE QObject* getToolbar(const QString& toolbarId);
+ Q_INVOKABLE QObject* getToolbar(const QString& toolbarId);
};
#endif // hifi_ToolbarScriptingInterface_h
diff --git a/interface/src/ui/OverlayConductor.cpp b/interface/src/ui/OverlayConductor.cpp
index 74e27122e4..6a99641ce4 100644
--- a/interface/src/ui/OverlayConductor.cpp
+++ b/interface/src/ui/OverlayConductor.cpp
@@ -32,7 +32,7 @@ bool OverlayConductor::headOutsideOverlay() const {
glm::vec3 uiPos = uiTransform.getTranslation();
glm::vec3 uiForward = uiTransform.getRotation() * glm::vec3(0.0f, 0.0f, -1.0f);
- const float MAX_COMPOSITOR_DISTANCE = 0.6f;
+ const float MAX_COMPOSITOR_DISTANCE = 0.99f; // If you're 1m from center of ui sphere, you're at the surface.
const float MAX_COMPOSITOR_ANGLE = 180.0f; // rotation check is effectively disabled
if (glm::distance(uiPos, hmdPos) > MAX_COMPOSITOR_DISTANCE ||
glm::dot(uiForward, hmdForward) < cosf(glm::radians(MAX_COMPOSITOR_ANGLE))) {
@@ -124,7 +124,7 @@ void OverlayConductor::update(float dt) {
_flags &= ~SuppressedByDrive;
}
} else {
- if (myAvatar->getClearOverlayWhenDriving() && drivingChanged && isDriving) {
+ if (myAvatar->getClearOverlayWhenMoving() && drivingChanged && isDriving) {
_flags |= SuppressedByDrive;
}
}
diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp
index 6decef3240..c1705da206 100644
--- a/interface/src/ui/PreferencesDialog.cpp
+++ b/interface/src/ui/PreferencesDialog.cpp
@@ -62,9 +62,9 @@ void setupPreferences() {
preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Snap turn when in HMD", getter, setter));
}
{
- auto getter = [=]()->bool {return myAvatar->getClearOverlayWhenDriving(); };
- auto setter = [=](bool value) { myAvatar->setClearOverlayWhenDriving(value); };
- preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Clear overlays when driving", getter, setter));
+ auto getter = [=]()->bool {return myAvatar->getClearOverlayWhenMoving(); };
+ auto setter = [=](bool value) { myAvatar->setClearOverlayWhenMoving(value); };
+ preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Clear overlays when moving", getter, setter));
}
{
auto getter = []()->QString { return Snapshot::snapshotsLocation.get(); };
diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp
index b21f5a0e84..8aee2245c0 100644
--- a/libraries/animation/src/Rig.cpp
+++ b/libraries/animation/src/Rig.cpp
@@ -163,7 +163,6 @@ void Rig::destroyAnimGraph() {
}
void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset) {
-
_geometryOffset = AnimPose(geometry.offset);
_invGeometryOffset = _geometryOffset.inverse();
setModelOffset(modelOffset);
@@ -1224,8 +1223,7 @@ void Rig::copyJointsIntoJointData(QVector& jointDataVec) const {
}
void Rig::copyJointsFromJointData(const QVector& jointDataVec) {
-
- if (_animSkeleton) {
+ if (_animSkeleton && jointDataVec.size() == (int)_internalPoseSet._overrideFlags.size()) {
// transform all the default poses into rig space.
const AnimPose geometryToRigPose(_geometryToRigTransform);
diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index 6d99d6ad81..709cc76d01 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -269,7 +269,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
_lastSentJointData.resize(_jointData.size());
for (int i=0; i < _jointData.size(); i++) {
- const JointData& data = _jointData.at(i);
+ const JointData& data = _jointData[i];
if (sendAll || _lastSentJointData[i].rotation != data.rotation) {
if (sendAll ||
!cullSmallChanges ||
@@ -294,7 +294,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
validityBit = 0;
validity = *validityPosition++;
for (int i = 0; i < _jointData.size(); i ++) {
- const JointData& data = _jointData[ i ];
+ const JointData& data = _jointData[i];
if (validity & (1 << validityBit)) {
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation);
}
@@ -317,7 +317,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
float maxTranslationDimension = 0.0;
for (int i=0; i < _jointData.size(); i++) {
- const JointData& data = _jointData.at(i);
+ const JointData& data = _jointData[i];
if (sendAll || _lastSentJointData[i].translation != data.translation) {
if (sendAll ||
!cullSmallChanges ||
@@ -348,7 +348,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
validityBit = 0;
validity = *validityPosition++;
for (int i = 0; i < _jointData.size(); i ++) {
- const JointData& data = _jointData[ i ];
+ const JointData& data = _jointData[i];
if (validity & (1 << validityBit)) {
destinationBuffer +=
packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX);
@@ -425,7 +425,6 @@ bool AvatarData::shouldLogError(const quint64& now) {
// read data in packet starting at byte offset and return number of bytes parsed
int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
-
// lazily allocate memory for HeadData in case we're not an Avatar instance
if (!_headData) {
_headData = new HeadData(this);
@@ -669,7 +668,9 @@ void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::v
}
JointData& data = _jointData[index];
data.rotation = rotation;
+ data.rotationSet = true;
data.translation = translation;
+ data.translationSet = true;
}
void AvatarData::clearJointData(int index) {
@@ -774,6 +775,7 @@ void AvatarData::setJointRotation(int index, const glm::quat& rotation) {
}
JointData& data = _jointData[index];
data.rotation = rotation;
+ data.rotationSet = true;
}
void AvatarData::setJointTranslation(int index, const glm::vec3& translation) {
@@ -789,6 +791,7 @@ void AvatarData::setJointTranslation(int index, const glm::vec3& translation) {
}
JointData& data = _jointData[index];
data.translation = translation;
+ data.translationSet = true;
}
void AvatarData::clearJointData(const QString& name) {
@@ -858,7 +861,6 @@ void AvatarData::setJointTranslations(QVector jointTranslations) {
"setJointTranslations", Qt::BlockingQueuedConnection,
Q_ARG(QVector, jointTranslations));
}
-
if (_jointData.size() < jointTranslations.size()) {
_jointData.resize(jointTranslations.size());
}
@@ -1100,6 +1102,7 @@ void AvatarData::sendIdentityPacket() {
void AvatarData::updateJointMappings() {
_jointIndices.clear();
_jointNames.clear();
+ _jointData.clear();
if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
@@ -1457,7 +1460,6 @@ void AvatarData::fromJson(const QJsonObject& json) {
auto joint = jointDataFromJsonValue(jointJson);
jointArray.push_back(joint);
setJointData(i, joint.rotation, joint.translation);
- _jointData[i].rotationSet = true; // Have to do that to broadcast the avatar new pose
i++;
}
setRawJointData(jointArray);
diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp
index 9084fd837b..d153cfd977 100644
--- a/libraries/avatars/src/AvatarHashMap.cpp
+++ b/libraries/avatars/src/AvatarHashMap.cpp
@@ -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();
}
diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h
index 5f58074427..9d3ebb60f5 100644
--- a/libraries/avatars/src/AvatarHashMap.h
+++ b/libraries/avatars/src/AvatarHashMap.h
@@ -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);
diff --git a/libraries/display-plugins/CMakeLists.txt b/libraries/display-plugins/CMakeLists.txt
index f2d58d825e..fe08647074 100644
--- a/libraries/display-plugins/CMakeLists.txt
+++ b/libraries/display-plugins/CMakeLists.txt
@@ -1,6 +1,6 @@
set(TARGET_NAME display-plugins)
setup_hifi_library(OpenGL)
-link_hifi_libraries(shared plugins gl gpu-gl ui)
+link_hifi_libraries(shared plugins ui-plugins gl gpu-gl ui)
target_opengl()
diff --git a/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.cpp b/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.cpp
index 4b8d957e5f..d068bef3b0 100644
--- a/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.cpp
+++ b/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.cpp
@@ -11,7 +11,7 @@
#include
#include "DisplayPlugin.h"
-#include
+#include
static Setting::Handle IPD_SCALE_HANDLE("hmd.ipdScale", 1.0f);
diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp
index 48dda1f73d..f488a805c6 100644
--- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp
@@ -13,7 +13,7 @@
#include
#include
-#include
+#include
const QString Basic2DWindowOpenGLDisplayPlugin::NAME("Desktop");
diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp
index a17fe2fd49..032350a07c 100644
--- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp
+++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp
@@ -425,7 +425,7 @@ glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const
d = glm::normalize(overlaySurfacePoint);
}
reticlePosition = headPosition + (d * getReticleDepth());
- quat reticleOrientation = quat(vec3(-spherical.y, spherical.x, 0.0f));
+ quat reticleOrientation = glm::quat_cast(_currentDisplayPlugin->getHeadPose());
vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize * getReticleDepth());
return glm::inverse(eyePose) * createMatFromScaleQuatAndPos(reticleScale, reticleOrientation, reticlePosition);
} else {
diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp
index 3f642072a0..4fadbdb94b 100644
--- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp
@@ -10,7 +10,7 @@
#include "NullDisplayPlugin.h"
#include
-#include
+#include
const QString NullDisplayPlugin::NAME("NullDisplayPlugin");
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
index b49b41d8b2..b72f52351f 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
@@ -22,12 +22,13 @@
#include
#include
#include
-#include
+#include
#include
#include
#include
#include
#include "CompositorHelper.h"
+#include
#if THREADED_PRESENT
@@ -202,6 +203,7 @@ private:
#endif
+
OpenGLDisplayPlugin::OpenGLDisplayPlugin() {
_sceneTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture){
cleanupForSceneTexture(texture);
@@ -234,10 +236,11 @@ bool OpenGLDisplayPlugin::activate() {
cursorData.hotSpot = vec2(0.5f);
}
}
-
+ if (!_container) {
+ return false;
+ }
_vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported();
-
#if THREADED_PRESENT
// Start the present thread if necessary
QSharedPointer presentThread;
@@ -282,7 +285,11 @@ bool OpenGLDisplayPlugin::activate() {
animation->start();
});
- return DisplayPlugin::activate();
+ if (isHmd() && (getHmdScreen() >= 0)) {
+ _container->showDisplayPluginsTools();
+ }
+
+ return Parent::activate();
}
void OpenGLDisplayPlugin::deactivate() {
@@ -301,7 +308,16 @@ void OpenGLDisplayPlugin::deactivate() {
_container->makeRenderingContextCurrent();
#endif
internalDeactivate();
- DisplayPlugin::deactivate();
+
+ _container->showDisplayPluginsTools(false);
+ if (!_container->currentDisplayActions().isEmpty()) {
+ auto menu = _container->getPrimaryMenu();
+ foreach(auto itemInfo, _container->currentDisplayActions()) {
+ menu->removeMenuItem(itemInfo.first, itemInfo.second);
+ }
+ _container->currentDisplayActions().clear();
+ }
+ Parent::deactivate();
}
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
index d8d4d8ff8c..068b236289 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
@@ -120,7 +120,7 @@ protected:
QMap _sceneTextureToFrameIndexMap;
uint32_t _currentPresentFrameIndex { 0 };
float _compositeOverlayAlpha{ 1.0f };
-
+
gpu::TexturePointer _currentSceneTexture;
gpu::TexturePointer _currentOverlayTexture;
@@ -165,4 +165,3 @@ private:
float _overlayAlpha{ 1.0f };
};
-
diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
index efdb68226b..f1aa1edc81 100644
--- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
@@ -16,7 +16,7 @@
#include
#include
-#include
+#include
#include
#include
#include
@@ -420,8 +420,9 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve
}
void HmdDisplayPlugin::compositeExtra() {
- std::array handLasers;
- std::array renderHandPoses;
+ const int NUMBER_OF_HANDS = 2;
+ std::array handLasers;
+ std::array renderHandPoses;
Transform uiModelTransform;
withPresentThreadLock([&] {
handLasers = _handLasers;
@@ -443,9 +444,9 @@ void HmdDisplayPlugin::compositeExtra() {
using namespace oglplus;
useProgram(_laserProgram);
_laserGeometry->Use();
- std::array handLaserModelMatrices;
+ std::array handLaserModelMatrices;
- for (int i = 0; i < 2; ++i) {
+ for (int i = 0; i < NUMBER_OF_HANDS; ++i) {
if (renderHandPoses[i] == identity) {
continue;
}
@@ -485,7 +486,7 @@ void HmdDisplayPlugin::compositeExtra() {
auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye);
auto view = glm::inverse(eyePose);
const auto& projection = _eyeProjections[eye];
- for (int i = 0; i < 2; ++i) {
+ for (int i = 0; i < NUMBER_OF_HANDS; ++i) {
if (handLaserModelMatrices[i] == identity) {
continue;
}
diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp
index 5f55841be1..5d9f812edf 100644
--- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp
@@ -9,7 +9,7 @@
#include "SideBySideStereoDisplayPlugin.h"
#include
#include
-#include
+#include
#include
#include "../CompositorHelper.h"
diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp
index 6c6716c8fa..cfdfb1fc21 100644
--- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp
@@ -15,7 +15,7 @@
#include
#include
-#include
+#include
#include
#include
#include "../CompositorHelper.h"
diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp
index 77a0c6d6fe..ec1f8a50bc 100644
--- a/libraries/entities/src/EntityTree.cpp
+++ b/libraries/entities/src/EntityTree.cpp
@@ -150,8 +150,10 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
if (!canRezPermanentEntities && (entity->getLifetime() != properties.getLifetime())) {
// we don't allow a Node that can't create permanent entities to adjust lifetimes on existing ones
- qCDebug(entities) << "Refusing disallowed entity lifetime adjustment.";
- return false;
+ if (properties.lifetimeChanged()) {
+ qCDebug(entities) << "Refusing disallowed entity lifetime adjustment.";
+ return false;
+ }
}
// enforce support for locked entities. If an entity is currently locked, then the only
@@ -322,8 +324,8 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
bool EntityTree::permissionsAllowRez(const EntityItemProperties& properties, bool canRez, bool canRezTmp) {
float lifeTime = properties.getLifetime();
- if (lifeTime == 0.0f || lifeTime > _maxTmpEntityLifetime) {
- // this is an attempt to rez a permanent entity.
+ if (lifeTime == ENTITY_ITEM_IMMORTAL_LIFETIME || lifeTime > _maxTmpEntityLifetime) {
+ // this is an attempt to rez a permanent or non-temporary entity.
if (!canRez) {
return false;
}
diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp
index 302e0b8515..79b39a2331 100644
--- a/libraries/gl/src/gl/GLHelpers.cpp
+++ b/libraries/gl/src/gl/GLHelpers.cpp
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
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();
diff --git a/libraries/gl/src/gl/GLHelpers.h b/libraries/gl/src/gl/GLHelpers.h
index ddb254f1c5..477bf7abc8 100644
--- a/libraries/gl/src/gl/GLHelpers.h
+++ b/libraries/gl/src/gl/GLHelpers.h
@@ -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
diff --git a/libraries/gpu-gl/src/gpu/gl/GLPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLPipeline.cpp
index 19cf798b19..fa54e7c8fe 100644
--- a/libraries/gpu-gl/src/gpu/gl/GLPipeline.cpp
+++ b/libraries/gpu-gl/src/gpu/gl/GLPipeline.cpp
@@ -24,8 +24,15 @@ GLPipeline* GLPipeline::sync(const Pipeline& pipeline) {
// No object allocated yet, let's see if it's worth it...
ShaderPointer shader = pipeline.getProgram();
+
+ // If this pipeline's shader has already failed to compile, don't try again
+ if (shader->compilationHasFailed()) {
+ return nullptr;
+ }
+
GLShader* programObject = GLShader::sync(*shader);
if (programObject == nullptr) {
+ shader->setCompilationHasFailed(true);
return nullptr;
}
diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h
index 59c6401150..e4643f2b7c 100755
--- a/libraries/gpu/src/gpu/Shader.h
+++ b/libraries/gpu/src/gpu/Shader.h
@@ -120,6 +120,9 @@ public:
bool isProgram() const { return getType() > NUM_DOMAINS; }
bool isDomain() const { return getType() < NUM_DOMAINS; }
+ void setCompilationHasFailed(bool compilationHasFailed) { _compilationHasFailed = compilationHasFailed; }
+ bool compilationHasFailed() const { return _compilationHasFailed; }
+
const Source& getSource() const { return _source; }
const Shaders& getShaders() const { return _shaders; }
@@ -180,6 +183,9 @@ protected:
// The type of the shader, the master key
Type _type;
+
+ // Whether or not the shader compilation failed
+ bool _compilationHasFailed { false };
};
typedef Shader::Pointer ShaderPointer;
diff --git a/libraries/input-plugins/CMakeLists.txt b/libraries/input-plugins/CMakeLists.txt
index b81554511d..b0ea13843b 100644
--- a/libraries/input-plugins/CMakeLists.txt
+++ b/libraries/input-plugins/CMakeLists.txt
@@ -1,5 +1,5 @@
set(TARGET_NAME input-plugins)
setup_hifi_library()
-link_hifi_libraries(shared plugins controllers)
+link_hifi_libraries(shared plugins ui-plugins controllers)
GroupSources("src/input-plugins")
diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp
index 26b3801ec1..b6c5e691a6 100644
--- a/libraries/networking/src/AccountManager.cpp
+++ b/libraries/networking/src/AccountManager.cpp
@@ -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(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);
diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h
index 4803d2625f..d30a05fb2c 100644
--- a/libraries/networking/src/AccountManager.h
+++ b/libraries/networking/src/AccountManager.h
@@ -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
diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp
index 80989acd2c..df9b4094b0 100644
--- a/libraries/networking/src/AddressManager.cpp
+++ b/libraries/networking/src/AddressManager.cpp
@@ -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;
diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h
index c153878a7e..de46a0dae5 100644
--- a/libraries/networking/src/NodePermissions.h
+++ b/libraries/networking/src/NodePermissions.h
@@ -24,9 +24,9 @@ using NodePermissionsPointer = std::shared_ptr;
class NodePermissions {
public:
NodePermissions() { _id = QUuid::createUuid().toString(); }
- NodePermissions(const QString& name) { _id = name; }
+ NodePermissions(const QString& name) { _id = name.toLower(); }
NodePermissions(QMap perms) {
- _id = perms["permissions_id"].toString();
+ _id = perms["permissions_id"].toString().toLower();
canConnectToDomain = perms["id_can_connect"].toBool();
canAdjustLocks = perms["id_can_adjust_locks"].toBool();
canRezPermanentEntities = perms["id_can_rez"].toBool();
@@ -38,7 +38,7 @@ public:
QString getID() const { return _id; }
// the _id member isn't authenticated and _username is.
- void setUserName(QString userName) { _userName = userName; }
+ void setUserName(QString userName) { _userName = userName.toLower(); }
QString getUserName() { return _userName; }
bool isAssignment { false };
@@ -88,6 +88,23 @@ protected:
QString _userName;
};
+
+// wrap QHash in a class that forces all keys to be lowercase
+class NodePermissionsMap {
+public:
+ NodePermissionsMap() { }
+ NodePermissionsPointer& operator[](const QString& key) { return _data[key.toLower()]; }
+ NodePermissionsPointer operator[](const QString& key) const { return _data.value(key.toLower()); }
+ bool contains(const QString& key) const { return _data.contains(key.toLower()); }
+ QList keys() const { return _data.keys(); }
+ QHash get() { return _data; }
+ void clear() { _data.clear(); }
+
+private:
+ QHash _data;
+};
+
+
const NodePermissions DEFAULT_AGENT_PERMISSIONS;
QDebug operator<<(QDebug debug, const NodePermissions& perms);
diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp
index 83c6eb304e..eba4d31167 100644
--- a/libraries/networking/src/UserActivityLogger.cpp
+++ b/libraries/networking/src/UserActivityLogger.cpp
@@ -18,6 +18,7 @@
#include "UserActivityLogger.h"
#include
+#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);
diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h
index c2ab93db2f..b41960a8ad 100644
--- a/libraries/networking/src/UserActivityLogger.h
+++ b/libraries/networking/src/UserActivityLogger.h
@@ -20,6 +20,7 @@
#include
#include
+#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);
diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp
new file mode 100644
index 0000000000..8b22b8ff58
--- /dev/null
+++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp
@@ -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));
+}
diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h
new file mode 100644
index 0000000000..9d60d666e2
--- /dev/null
+++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h
@@ -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
+#include
+
+#include
+
+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
diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp
index 8f22c576f0..dc57a82fd3 100644
--- a/libraries/physics/src/EntityMotionState.cpp
+++ b/libraries/physics/src/EntityMotionState.cpp
@@ -135,7 +135,14 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) {
_nextOwnershipBid = 0;
}
if ((flags & Simulation::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) {
- _body->activate();
+ if (_body->isKinematicObject()) {
+ // only force activate kinematic bodies (dynamic shouldn't need force and
+ // active static bodies are special (see PhysicsEngine::_activeStaticBodies))
+ _body->activate(true);
+ _lastKinematicStep = ObjectMotionState::getWorldSimulationStep();
+ } else {
+ _body->activate();
+ }
}
}
diff --git a/libraries/plugins/src/plugins/DisplayPlugin.cpp b/libraries/plugins/src/plugins/DisplayPlugin.cpp
index a217041f4e..747c72c08e 100644
--- a/libraries/plugins/src/plugins/DisplayPlugin.cpp
+++ b/libraries/plugins/src/plugins/DisplayPlugin.cpp
@@ -1,28 +1,6 @@
#include "DisplayPlugin.h"
#include
-#include
-
-#include "PluginContainer.h"
-
-bool DisplayPlugin::activate() {
- if (isHmd() && (getHmdScreen() >= 0)) {
- _container->showDisplayPluginsTools();
- }
- return Parent::activate();
-}
-
-void DisplayPlugin::deactivate() {
- _container->showDisplayPluginsTools(false);
- if (!_container->currentDisplayActions().isEmpty()) {
- auto menu = _container->getPrimaryMenu();
- foreach(auto itemInfo, _container->currentDisplayActions()) {
- menu->removeMenuItem(itemInfo.first, itemInfo.second);
- }
- _container->currentDisplayActions().clear();
- }
- Parent::deactivate();
-}
int64_t DisplayPlugin::getPaintDelayUsecs() const {
std::lock_guard lock(_paintDelayMutex);
diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h
index 72bb6a315c..f0ba762ecb 100644
--- a/libraries/plugins/src/plugins/DisplayPlugin.h
+++ b/libraries/plugins/src/plugins/DisplayPlugin.h
@@ -132,8 +132,6 @@ public:
Present = QEvent::User + 1
};
- bool activate() override;
- void deactivate() override;
virtual bool isHmd() const { return false; }
virtual int getHmdScreen() const { return -1; }
/// By default, all HMDs are stereo
diff --git a/libraries/plugins/src/plugins/Plugin.h b/libraries/plugins/src/plugins/Plugin.h
index fb5bf0ba55..0452c7fbfe 100644
--- a/libraries/plugins/src/plugins/Plugin.h
+++ b/libraries/plugins/src/plugins/Plugin.h
@@ -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 };
diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp
index 7161132c5e..29658eeb6b 100644
--- a/libraries/plugins/src/plugins/PluginManager.cpp
+++ b/libraries/plugins/src/plugins/PluginManager.cpp
@@ -14,10 +14,12 @@
#include
#include
+#include
+#include
+
#include "RuntimePlugin.h"
#include "DisplayPlugin.h"
#include "InputPlugin.h"
-#include "PluginContainer.h"
PluginManager* PluginManager::getInstance() {
@@ -120,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,9 +144,10 @@ const DisplayPluginList& PluginManager::getDisplayPlugins() {
}
}
}
- auto& container = PluginContainer::getInstance();
for (auto plugin : displayPlugins) {
- plugin->setContainer(&container);
+ connect(plugin.get(), &Plugin::deviceConnected, this, deviceAddedCallback, Qt::QueuedConnection);
+ connect(plugin.get(), &Plugin::subdeviceConnected, this, subdeviceAddedCallback, Qt::QueuedConnection);
+ plugin->setContainer(_container);
plugin->init();
}
@@ -156,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();
@@ -171,9 +192,10 @@ const InputPluginList& PluginManager::getInputPlugins() {
}
}
- auto& container = PluginContainer::getInstance();
for (auto plugin : inputPlugins) {
- plugin->setContainer(&container);
+ connect(plugin.get(), &Plugin::deviceConnected, this, deviceAddedCallback, Qt::QueuedConnection);
+ connect(plugin.get(), &Plugin::subdeviceConnected, this, subdeviceAddedCallback, Qt::QueuedConnection);
+ plugin->setContainer(_container);
plugin->init();
}
});
diff --git a/libraries/plugins/src/plugins/PluginManager.h b/libraries/plugins/src/plugins/PluginManager.h
index 2a94e6490b..7903bdd724 100644
--- a/libraries/plugins/src/plugins/PluginManager.h
+++ b/libraries/plugins/src/plugins/PluginManager.h
@@ -26,4 +26,7 @@ public:
void disableDisplays(const QStringList& displays);
void disableInputs(const QStringList& inputs);
void saveSettings();
+ void setContainer(PluginContainer* container) { _container = container; }
+private:
+ PluginContainer* _container { nullptr };
};
diff --git a/libraries/shared/src/GPUIdent.cpp b/libraries/shared/src/GPUIdent.cpp
index 19838964a4..02f92d87e7 100644
--- a/libraries/shared/src/GPUIdent.cpp
+++ b/libraries/shared/src/GPUIdent.cpp
@@ -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) {
diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp
index b80fac637c..edb6fe437d 100644
--- a/libraries/shared/src/SharedUtil.cpp
+++ b/libraries/shared/src/SharedUtil.cpp
@@ -28,6 +28,7 @@
#ifdef Q_OS_WIN
#include "CPUIdent.h"
+#include
#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(&pmc), sizeof(pmc))) {
+ return false;
+ }
+ info.processUsedMemoryBytes = pmc.PrivateUsage;
+ info.processPeakUsedMemoryBytes = pmc.PeakPagefileUsage;
+
+ return true;
+#endif
+
+ return false;
+}
\ No newline at end of file
diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h
index 042396f474..f3e5625484 100644
--- a/libraries/shared/src/SharedUtil.h
+++ b/libraries/shared/src/SharedUtil.h
@@ -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
diff --git a/libraries/ui-plugins/CMakeLists.txt b/libraries/ui-plugins/CMakeLists.txt
new file mode 100644
index 0000000000..9ce189b117
--- /dev/null
+++ b/libraries/ui-plugins/CMakeLists.txt
@@ -0,0 +1,3 @@
+set(TARGET_NAME ui-plugins)
+setup_hifi_library(OpenGL)
+link_hifi_libraries(shared plugins ui)
diff --git a/libraries/plugins/src/plugins/PluginContainer.cpp b/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp
similarity index 100%
rename from libraries/plugins/src/plugins/PluginContainer.cpp
rename to libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp
diff --git a/libraries/plugins/src/plugins/PluginContainer.h b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h
similarity index 94%
rename from libraries/plugins/src/plugins/PluginContainer.h
rename to libraries/ui-plugins/src/ui-plugins/PluginContainer.h
index e1d1a212e2..74ac834057 100644
--- a/libraries/plugins/src/plugins/PluginContainer.h
+++ b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h
@@ -16,7 +16,7 @@
#include
#include
-#include "Forward.h"
+#include
class QAction;
class GLWidget;
@@ -63,8 +63,8 @@ public:
virtual GLWidget* getPrimaryWidget() = 0;
virtual MainWindow* getPrimaryWindow() = 0;
virtual QOpenGLContext* getPrimaryContext() = 0;
- virtual bool isForeground() = 0;
- virtual const DisplayPluginPointer getActiveDisplayPlugin() const = 0;
+ virtual bool isForeground() const = 0;
+ virtual DisplayPluginPointer getActiveDisplayPlugin() const = 0;
/// settings interface
bool getBoolSetting(const QString& settingName, bool defaultValue);
@@ -84,3 +84,4 @@ protected:
std::map _exclusiveGroups;
QRect _savedGeometry { 10, 120, 800, 600 };
};
+
diff --git a/plugins/hifiNeuron/src/NeuronPlugin.cpp b/plugins/hifiNeuron/src/NeuronPlugin.cpp
index 0a4bc7f8d2..e41472a8c5 100644
--- a/plugins/hifiNeuron/src/NeuronPlugin.cpp
+++ b/plugins/hifiNeuron/src/NeuronPlugin.cpp
@@ -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;
}
diff --git a/plugins/hifiSdl2/src/SDL2Manager.cpp b/plugins/hifiSdl2/src/SDL2Manager.cpp
index 09e783864c..b9a19658e2 100644
--- a/plugins/hifiSdl2/src/SDL2Manager.cpp
+++ b/plugins/hifiSdl2/src/SDL2Manager.cpp
@@ -66,6 +66,7 @@ void SDL2Manager::init() {
auto userInputMapper = DependencyManager::get();
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)) {
diff --git a/plugins/hifiSixense/CMakeLists.txt b/plugins/hifiSixense/CMakeLists.txt
index 589b5b8964..f907d7865f 100644
--- a/plugins/hifiSixense/CMakeLists.txt
+++ b/plugins/hifiSixense/CMakeLists.txt
@@ -8,5 +8,5 @@
set(TARGET_NAME hifiSixense)
setup_hifi_plugin(Script Qml Widgets)
-link_hifi_libraries(shared controllers ui plugins input-plugins)
+link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins)
target_sixense()
diff --git a/plugins/hifiSixense/src/SixenseManager.cpp b/plugins/hifiSixense/src/SixenseManager.cpp
index 9ea79a8b96..03028249a3 100644
--- a/plugins/hifiSixense/src/SixenseManager.cpp
+++ b/plugins/hifiSixense/src/SixenseManager.cpp
@@ -28,7 +28,7 @@
#include
#include
#include
-#include
+#include
#include
#include
@@ -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();
userInputMapper->withLock([&, this]() {
_inputDevice->update(deltaTime, inputCalibrationData);
diff --git a/plugins/hifiSpacemouse/src/SpacemouseManager.cpp b/plugins/hifiSpacemouse/src/SpacemouseManager.cpp
index 3d9b93ff44..4641799b79 100644
--- a/plugins/hifiSpacemouse/src/SpacemouseManager.cpp
+++ b/plugins/hifiSpacemouse/src/SpacemouseManager.cpp
@@ -18,7 +18,6 @@
#include
#include
-#include
#include
const QString SpacemouseManager::NAME { "Spacemouse" };
@@ -59,7 +58,6 @@ bool SpacemouseManager::activate() {
if (instance->getDeviceID() == controller::Input::INVALID_DEVICE) {
auto userInputMapper = DependencyManager::get();
userInputMapper->registerDevice(instance);
- UserActivityLogger::getInstance().connectedDevice("controller", NAME);
}
return true;
}
@@ -330,7 +328,6 @@ bool SpacemouseManager::RawInputEventFilter(void* msg, long* result) {
auto userInputMapper = DependencyManager::get();
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());
@@ -857,7 +854,7 @@ void SpacemouseManager::init() {
if (Is3dmouseAttached() && instance->getDeviceID() == controller::Input::INVALID_DEVICE) {
auto userInputMapper = DependencyManager::get();
userInputMapper->registerDevice(instance);
- UserActivityLogger::getInstance().connectedDevice("controller", "Spacemouse");
+ emit deviceConnected(getName());
}
//let one axis be dominant
//ConnexionClientControl(fConnexionClientID, kConnexionCtlSetSwitches, kConnexionSwitchDominant | kConnexionSwitchEnableAll, NULL);
diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt
index a91690ecdd..778be08dcf 100644
--- a/plugins/oculus/CMakeLists.txt
+++ b/plugins/oculus/CMakeLists.txt
@@ -13,7 +13,7 @@ if (WIN32)
set(TARGET_NAME oculus)
setup_hifi_plugin(Multimedia)
- link_hifi_libraries(shared gl gpu controllers ui plugins display-plugins input-plugins audio-client networking)
+ link_hifi_libraries(shared gl gpu controllers ui plugins ui-plugins display-plugins input-plugins audio-client networking)
include_hifi_library_headers(octree)
diff --git a/plugins/oculus/src/OculusControllerManager.cpp b/plugins/oculus/src/OculusControllerManager.cpp
index 0e9ca21804..b3b1b20b2b 100644
--- a/plugins/oculus/src/OculusControllerManager.cpp
+++ b/plugins/oculus/src/OculusControllerManager.cpp
@@ -13,7 +13,7 @@
#include
-#include
+#include
#include
#include
diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp
index 1006d69f06..2b2ec5bdb0 100644
--- a/plugins/oculus/src/OculusDisplayPlugin.cpp
+++ b/plugins/oculus/src/OculusDisplayPlugin.cpp
@@ -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((currentDebugMode + 1) % ovrPerfHud_Count);
diff --git a/plugins/oculus/src/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h
index d6cd6f6f3d..ed6e0d13ea 100644
--- a/plugins/oculus/src/OculusDisplayPlugin.h
+++ b/plugins/oculus/src/OculusDisplayPlugin.h
@@ -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;
diff --git a/plugins/oculusLegacy/CMakeLists.txt b/plugins/oculusLegacy/CMakeLists.txt
index a4e00013f1..c1f2c6249f 100644
--- a/plugins/oculusLegacy/CMakeLists.txt
+++ b/plugins/oculusLegacy/CMakeLists.txt
@@ -12,7 +12,7 @@ if (APPLE)
set(TARGET_NAME oculusLegacy)
setup_hifi_plugin()
- link_hifi_libraries(shared gl gpu plugins ui display-plugins input-plugins)
+ link_hifi_libraries(shared gl gpu plugins ui ui-plugins display-plugins input-plugins)
include_hifi_library_headers(octree)
diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp
index 699891deaa..2c032f7005 100644
--- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp
+++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp
@@ -26,7 +26,7 @@
#include
#include
-#include "plugins/PluginContainer.h"
+#include
#include "OculusHelpers.h"
using namespace oglplus;
@@ -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);
}
diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h
index 453a6f9168..6ffc1a7f44 100644
--- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h
+++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h
@@ -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
diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt
index 1ba8d05b92..8263e87767 100644
--- a/plugins/openvr/CMakeLists.txt
+++ b/plugins/openvr/CMakeLists.txt
@@ -12,7 +12,7 @@ if (WIN32)
set(TARGET_NAME openvr)
setup_hifi_plugin(OpenGL Script Qml Widgets)
link_hifi_libraries(shared gl networking controllers ui
- plugins display-plugins input-plugins script-engine
+ plugins display-plugins ui-plugins input-plugins script-engine
render-utils model gpu render model-networking fbx)
include_hifi_library_headers(octree)
diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp
index 9c4313dc13..5233ad644a 100644
--- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp
+++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp
@@ -20,7 +20,7 @@
#include
#include
-#include
+#include
#include
#include
#include
@@ -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);
@@ -267,7 +273,7 @@ void OpenVrDisplayPlugin::unsuppressKeyboard() {
return;
}
if (1 == _keyboardSupressionCount.fetch_sub(1)) {
- enableOpenVrKeyboard();
+ enableOpenVrKeyboard(_container);
}
}
diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h
index b22fadb52c..fca4dab9e9 100644
--- a/plugins/openvr/src/OpenVrDisplayPlugin.h
+++ b/plugins/openvr/src/OpenVrDisplayPlugin.h
@@ -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;
diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp
index 4f02c0384d..e71c8942d6 100644
--- a/plugins/openvr/src/OpenVrHelpers.cpp
+++ b/plugins/openvr/src/OpenVrHelpers.cpp
@@ -21,6 +21,9 @@
#include
#include
#include
+#include
+#include
+#include "../../interface/src/Menu.h"
Q_DECLARE_LOGGING_CATEGORY(displayplugins)
Q_LOGGING_CATEGORY(displayplugins, "hifi.plugins.display")
@@ -91,7 +94,7 @@ void releaseOpenVrSystem() {
static char textArray[8192];
-static QMetaObject::Connection _focusConnection, _focusTextConnection;
+static QMetaObject::Connection _focusConnection, _focusTextConnection, _overlayMenuConnection;
extern bool _openVrDisplayActive;
static vr::IVROverlay* _overlay { nullptr };
static QObject* _keyboardFocusObject { nullptr };
@@ -99,7 +102,8 @@ static QString _existingText;
static Qt::InputMethodHints _currentHints;
extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount];
static bool _keyboardShown { false };
-static const uint32_t SHOW_KEYBOARD_DELAY_MS = 100;
+static bool _overlayRevealed { false };
+static const uint32_t SHOW_KEYBOARD_DELAY_MS = 400;
void showOpenVrKeyboard(bool show = true) {
if (!_overlay) {
@@ -175,13 +179,26 @@ void finishOpenVrKeyboardInput() {
static const QString DEBUG_FLAG("HIFI_DISABLE_STEAM_VR_KEYBOARD");
bool disableSteamVrKeyboard = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
-void enableOpenVrKeyboard() {
+void enableOpenVrKeyboard(PluginContainer* container) {
if (disableSteamVrKeyboard) {
return;
}
auto offscreenUi = DependencyManager::get();
_overlay = vr::VROverlay();
+
+ auto menu = container->getPrimaryMenu();
+ auto action = menu->getActionForOption(MenuOption::Overlays);
+
+ // When the overlays are revealed, suppress the keyboard from appearing on text focus for a tenth of a second.
+ _overlayMenuConnection = QObject::connect(action, &QAction::triggered, [action] {
+ if (action->isChecked()) {
+ _overlayRevealed = true;
+ const int KEYBOARD_DELAY_MS = 100;
+ QTimer::singleShot(KEYBOARD_DELAY_MS, [&] { _overlayRevealed = false; });
+ }
+ });
+
_focusConnection = QObject::connect(offscreenUi->getWindow(), &QQuickWindow::focusObjectChanged, [](QObject* object) {
if (object != _keyboardFocusObject) {
showOpenVrKeyboard(false);
@@ -190,6 +207,11 @@ void enableOpenVrKeyboard() {
_focusTextConnection = QObject::connect(offscreenUi.data(), &OffscreenUi::focusTextChanged, [](bool focusText) {
if (_openVrDisplayActive) {
+ if (_overlayRevealed) {
+ // suppress at most one text focus event
+ _overlayRevealed = false;
+ return;
+ }
showOpenVrKeyboard(focusText);
}
});
@@ -200,6 +222,7 @@ void disableOpenVrKeyboard() {
if (disableSteamVrKeyboard) {
return;
}
+ QObject::disconnect(_overlayMenuConnection);
QObject::disconnect(_focusTextConnection);
QObject::disconnect(_focusConnection);
}
@@ -325,4 +348,4 @@ controller::Pose openVrControllerPoseToHandPose(bool isLeftHand, const mat4& mat
result.velocity = linearVelocity + glm::cross(angularVelocity, position - extractTranslation(mat));
result.angularVelocity = angularVelocity;
return result;
-}
\ No newline at end of file
+}
diff --git a/plugins/openvr/src/OpenVrHelpers.h b/plugins/openvr/src/OpenVrHelpers.h
index 131db517ab..19c9cbfff5 100644
--- a/plugins/openvr/src/OpenVrHelpers.h
+++ b/plugins/openvr/src/OpenVrHelpers.h
@@ -13,6 +13,7 @@
#include
#include
+#include
bool openVrSupported();
@@ -20,7 +21,7 @@ vr::IVRSystem* acquireOpenVrSystem();
void releaseOpenVrSystem();
void handleOpenVrEvents();
bool openVrQuitRequested();
-void enableOpenVrKeyboard();
+void enableOpenVrKeyboard(PluginContainer* container);
void disableOpenVrKeyboard();
bool isOpenVrKeyboardShown();
diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp
index 7f78ab8553..4a515978c3 100644
--- a/plugins/openvr/src/ViveControllerManager.cpp
+++ b/plugins/openvr/src/ViveControllerManager.cpp
@@ -18,7 +18,7 @@
#include
#include
#include
-#include
+#include
#include
#include
@@ -63,7 +63,7 @@ bool ViveControllerManager::activate() {
}
Q_ASSERT(_system);
- enableOpenVrKeyboard();
+ enableOpenVrKeyboard(_container);
// OpenVR provides 3d mesh representations of the controllers
// Disabled controller rendering code
@@ -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");
}
}
diff --git a/scripts/developer/tests/toolbarTest.js b/scripts/developer/tests/toolbarTest.js
index 20f349929f..e21fbd8e19 100644
--- a/scripts/developer/tests/toolbarTest.js
+++ b/scripts/developer/tests/toolbarTest.js
@@ -13,7 +13,7 @@ var toolBar = (function() {
newZoneButton,
newParticleButton
- var toolIconUrl = Script.resolvePath("assets/images/tools/");
+ var toolIconUrl = Script.resolvePath("../../system/assets/images/tools/");
function initialize() {
print("Toolbars: " + Toolbars);
diff --git a/scripts/system/away.js b/scripts/system/away.js
index 51d6277e21..04263d4223 100644
--- a/scripts/system/away.js
+++ b/scripts/system/away.js
@@ -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) {
diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js
index 1772e55015..26f977f9b0 100644
--- a/scripts/system/controllers/handControllerGrab.js
+++ b/scripts/system/controllers/handControllerGrab.js
@@ -868,6 +868,7 @@ function MyController(hand) {
};
this.createHotspots = function () {
+ var props, overlay;
var HAND_SPHERE_COLOR = { red: 90, green: 255, blue: 90 };
var HAND_SPHERE_ALPHA = 0.7;
@@ -884,7 +885,7 @@ function MyController(hand) {
if (DRAW_HAND_SPHERES) {
// add tiny spheres around the palm.
var handPosition = this.getHandPosition();
- var overlay = Overlays.addOverlay("sphere", {
+ overlay = Overlays.addOverlay("sphere", {
position: handPosition,
size: HAND_SPHERE_RADIUS * 2,
color: HAND_SPHERE_COLOR,
@@ -909,7 +910,7 @@ function MyController(hand) {
var _this = this;
this.entityPropertyCache.getEntities().forEach(function (entityID) {
if (_this.entityIsEquippableWithoutDistanceCheck(entityID)) {
- var props = _this.entityPropertyCache.getProps(entityID);
+ props = _this.entityPropertyCache.getProps(entityID);
overlay = Overlays.addOverlay("sphere", {
rotation: props.rotation,
@@ -931,7 +932,7 @@ function MyController(hand) {
}
if (DRAW_GRAB_SPHERES && _this.entityIsGrabbable(entityID)) {
- var props = _this.entityPropertyCache.getProps(entityID);
+ props = _this.entityPropertyCache.getProps(entityID);
overlay = Overlays.addOverlay("sphere", {
rotation: props.rotation,
@@ -956,16 +957,17 @@ function MyController(hand) {
this.updateHotspots = function() {
var _this = this;
+ var props;
this.hotspotOverlays.forEach(function (overlayInfo) {
if (overlayInfo.type === "hand") {
Overlays.editOverlay(overlayInfo.overlay, { position: _this.getHandPosition() });
} else if (overlayInfo.type === "equip") {
_this.entityPropertyCache.updateEntity(overlayInfo.entityID);
- var props = _this.entityPropertyCache.getProps(overlayInfo.entityID);
+ props = _this.entityPropertyCache.getProps(overlayInfo.entityID);
Overlays.editOverlay(overlayInfo.overlay, { position: props.position, rotation: props.rotation });
} else if (overlayInfo.type === "near") {
_this.entityPropertyCache.updateEntity(overlayInfo.entityID);
- var props = _this.entityPropertyCache.getProps(overlayInfo.entityID);
+ props = _this.entityPropertyCache.getProps(overlayInfo.entityID);
Overlays.editOverlay(overlayInfo.overlay, { position: props.position, rotation: props.rotation });
}
});
diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js
index 0f1e23b45c..374be0d1a1 100644
--- a/scripts/system/controllers/handControllerPointer.js
+++ b/scripts/system/controllers/handControllerPointer.js
@@ -1,6 +1,6 @@
"use strict";
/*jslint vars: true, plusplus: true*/
-/*globals Script, Overlays, Controller, Reticle, HMD, Camera, Entities, MyAvatar, Settings, Menu, ScriptDiscoveryService, Window, Vec3, Quat, print */
+/*globals Script, Overlays, Controller, Reticle, HMD, Camera, Entities, MyAvatar, Settings, Menu, ScriptDiscoveryService, Window, Vec3, Quat, print*/
//
// handControllerPointer.js
@@ -14,15 +14,11 @@
//
// Control the "mouse" using hand controller. (HMD and desktop.)
-// For now:
-// Hydra thumb button 3 is left-mouse, button 4 is right-mouse.
-// A click in the center of the vive thumb pad is left mouse. Vive menu button is context menu (right mouse).
// First-person only.
// Starts right handed, but switches to whichever is free: Whichever hand was NOT most recently squeezed.
// (For now, the thumb buttons on both controllers are always on.)
-// When over a HUD element, the reticle is shown where the active hand controller beam intersects the HUD.
-// Otherwise, the active hand controller shows a red ball where a click will act.
-
+// When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand
+// controller beam intersects the HUD.
// UTILITIES -------------
@@ -89,20 +85,15 @@ function Trigger(label) {
state = 'full';
} else if (that.triggerSmoothedReleased()) {
state = null;
- // These depend on previous state:
- // null -> squeezed ==> partial
- // full -> !squeezed ==> partial
- // Otherwise no change.
} else if (that.triggerSmoothedSqueezed()) {
- if (!state) {
- state = 'partial';
- }
- } else if (state === 'full') {
+ // Another way to do this would be to have hysteresis in this branch, but that seems to make things harder to use.
+ // In particular, the vive has a nice detent as you release off of full, and we want that to be a transition from
+ // full to partial.
state = 'partial';
}
that.state = state;
};
- // Answer a controller source function (answering either 0.0 or 1.0), with hysteresis.
+ // Answer a controller source function (answering either 0.0 or 1.0).
that.partial = function () {
return that.state ? 1.0 : 0.0; // either 'partial' or 'full'
};
@@ -369,24 +360,9 @@ clickMapping.enable();
// VISUAL AID -----------
// Same properties as handControllerGrab search sphere
-var BALL_SIZE = 0.011;
-var BALL_ALPHA = 0.5;
-var LASER_SEARCH_COLOR_XYZW = {x: 10 / 255, y: 10 / 255, z: 255 / 255, w: BALL_ALPHA};
-var LASER_TRIGGER_COLOR_XYZW = {x: 250 / 255, y: 10 / 255, z: 10 / 255, w: BALL_ALPHA};
-var fakeProjectionBall = Overlays.addOverlay("sphere", {
- size: 5 * BALL_SIZE,
- color: {red: 255, green: 10, blue: 10},
- ignoreRayIntersection: true,
- alpha: BALL_ALPHA,
- visible: false,
- solid: true,
- drawInFront: true // Even when burried inside of something, show it.
-});
-var overlays = [fakeProjectionBall]; // If we want to try showing multiple balls and lasers.
-Script.scriptEnding.connect(function () {
- overlays.forEach(Overlays.deleteOverlay);
-});
-var visualizationIsShowing = false; // Not whether it desired, but simply whether it is. Just an optimziation.
+var LASER_ALPHA = 0.5;
+var LASER_SEARCH_COLOR_XYZW = {x: 10 / 255, y: 10 / 255, z: 255 / 255, w: LASER_ALPHA};
+var LASER_TRIGGER_COLOR_XYZW = {x: 250 / 255, y: 10 / 255, z: 10 / 255, w: LASER_ALPHA};
var SYSTEM_LASER_DIRECTION = {x: 0, y: 0, z: -1};
var systemLaserOn = false;
function clearSystemLaser() {
@@ -401,81 +377,30 @@ function setColoredLaser() { // answer trigger state if lasers supported, else f
return HMD.setHandLasers(activeHudLaser, true, color, SYSTEM_LASER_DIRECTION) && activeTrigger.state;
}
-function turnOffVisualization(optionalEnableClicks) { // because we're showing cursor on HUD
- if (!optionalEnableClicks) {
- expireMouseCursor();
- clearSystemLaser();
- } else if (activeTrigger.state && (!systemLaserOn || (systemLaserOn !== activeTrigger.state))) { // last=>wrong color
- // If the active plugin doesn't implement hand lasers, show the mouse reticle instead.
- systemLaserOn = setColoredLaser();
- Reticle.visible = !systemLaserOn;
- } else if ((systemLaserOn || Reticle.visible) && !activeTrigger.state) {
- clearSystemLaser();
- Reticle.visible = false;
- }
-
- if (!visualizationIsShowing) {
- return;
- }
- visualizationIsShowing = false;
- overlays.forEach(function (overlay) {
- Overlays.editOverlay(overlay, {visible: false});
- });
-}
-var MAX_RAY_SCALE = 32000; // Anything large. It's a scale, not a distance.
-function updateVisualization(controllerPosition, controllerDirection, hudPosition3d, hudPosition2d) {
- ignore(controllerPosition, controllerDirection, hudPosition2d);
- clearSystemLaser();
- // Show an indication of where the cursor will appear when crossing a HUD element,
- // and where in-world clicking will occur.
- //
- // There are a number of ways we could do this, but for now, it's a blue sphere that rolls along
- // the HUD surface, and a red sphere that rolls along the 3d objects that will receive the click.
- // We'll leave it to other scripts (like handControllerGrab) to show a search beam when desired.
-
- function intersection3d(position, direction) {
- // Answer in-world intersection (entity or 3d overlay), or way-out point
- var pickRay = {origin: position, direction: direction};
- var result = findRayIntersection(pickRay);
- return result.intersects ? result.intersection : Vec3.sum(position, Vec3.multiply(MAX_RAY_SCALE, direction));
- }
-
- visualizationIsShowing = true;
- // We'd rather in-world interactions be done at the termination of the hand beam
- // -- intersection3d(controllerPosition, controllerDirection). Maybe have handControllerGrab
- // direclty manipulate both entity and 3d overlay objects.
- // For now, though, we present a false projection of the cursor onto whatever is below it. This is
- // different from the hand beam termination because the false projection is from the camera, while
- // the hand beam termination is from the hand.
- /* // FIXME: We can tighten this up later, once we know what will and won't be included.
- var eye = Camera.getPosition();
- var falseProjection = intersection3d(eye, Vec3.subtract(hudPosition3d, eye));
- Overlays.editOverlay(fakeProjectionBall, {visible: true, position: falseProjection});
- */
- Reticle.visible = false;
-
- return visualizationIsShowing; // In case we change caller to act conditionally.
-}
// MAIN OPERATIONS -----------
//
function update() {
var now = Date.now();
+ function off() {
+ expireMouseCursor();
+ clearSystemLaser();
+ }
if (!handControllerLockOut.expired(now)) {
- return turnOffVisualization(); // Let them use mouse it in peace.
+ return off(); // Let them use mouse it in peace.
}
if (!Menu.isOptionChecked("First Person")) {
- return turnOffVisualization(); // What to do? menus can be behind hand!
+ return off(); // What to do? menus can be behind hand!
}
if (!Window.hasFocus() || !Reticle.allowMouseCapture) {
- return turnOffVisualization(); // Don't mess with other apps or paused mouse activity
+ return off(); // Don't mess with other apps or paused mouse activity
}
leftTrigger.update();
rightTrigger.update();
var controllerPose = Controller.getPoseValue(activeHand);
// Valid if any plugged-in hand controller is "on". (uncradled Hydra, green-lighted Vive...)
if (!controllerPose.valid) {
- return turnOffVisualization(); // Controller is cradled.
+ return off(); // Controller is cradled.
}
var controllerPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, controllerPose.translation),
MyAvatar.position);
@@ -487,7 +412,7 @@ function update() {
if (Menu.isOptionChecked("Overlays")) { // With our hud resetting strategy, hudPoint3d should be valid here
print('Controller is parallel to HUD'); // so let us know that our assumptions are wrong.
}
- return turnOffVisualization();
+ return off();
}
var hudPoint2d = overlayFromWorldPoint(hudPoint3d);
@@ -499,13 +424,22 @@ function update() {
if (HMD.active) {
Reticle.depth = hudReticleDistance();
}
- return turnOffVisualization(true);
+ if (activeTrigger.state && (!systemLaserOn || (systemLaserOn !== activeTrigger.state))) { // last=>wrong color
+ // If the active plugin doesn't implement hand lasers, show the mouse reticle instead.
+ systemLaserOn = setColoredLaser();
+ Reticle.visible = !systemLaserOn;
+ } else if ((systemLaserOn || Reticle.visible) && !activeTrigger.state) {
+ clearSystemLaser();
+ Reticle.visible = false;
+ }
+ return;
}
// We are not pointing at a HUD element (but it could be a 3d overlay).
if (!activeTrigger.state) {
- return turnOffVisualization(); // No trigger (with hysteresis).
+ return off(); // No trigger
}
- updateVisualization(controllerPosition, controllerDirection, hudPoint3d, hudPoint2d);
+ clearSystemLaser();
+ Reticle.visible = false;
}
var UPDATE_INTERVAL = 50; // milliseconds. Script.update is too frequent.
diff --git a/scripts/system/edit.js b/scripts/system/edit.js
index b39c843411..d1ac32e6f4 100644
--- a/scripts/system/edit.js
+++ b/scripts/system/edit.js
@@ -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);
diff --git a/scripts/system/examples.js b/scripts/system/examples.js
index f850a9789e..a948f9e563 100644
--- a/scripts/system/examples.js
+++ b/scripts/system/examples.js
@@ -33,6 +33,8 @@ function showExamples(marketplaceID) {
print("setting examples URL to " + url);
examplesWindow.setURL(url);
examplesWindow.setVisible(true);
+
+ UserActivityLogger.openedMarketplace();
}
function hideExamples() {
diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html
index 82387cafa5..0af199ef56 100644
--- a/scripts/system/html/entityProperties.html
+++ b/scripts/system/html/entityProperties.html
@@ -452,6 +452,7 @@
var elTextText = document.getElementById("property-text-text");
var elTextLineHeight = document.getElementById("property-text-line-height");
var elTextTextColor = document.getElementById("property-text-text-color");
+ var elTextFaceCamera = document.getElementById("property-text-face-camera");
var elTextTextColorRed = document.getElementById("property-text-text-color-red");
var elTextTextColorGreen = document.getElementById("property-text-text-color-green");
var elTextTextColorBlue = document.getElementById("property-text-text-color-blue");
@@ -726,6 +727,7 @@
elTextText.value = properties.text;
elTextLineHeight.value = properties.lineHeight.toFixed(4);
+ elTextFaceCamera = properties.faceCamera;
elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red + "," + properties.textColor.green + "," + properties.textColor.blue + ")";
elTextTextColorRed.value = properties.textColor.red;
elTextTextColorGreen.value = properties.textColor.green;
@@ -988,8 +990,8 @@
elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures'));
elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text'));
+ elTextFaceCamera.addEventListener('change', createEmitCheckedPropertyUpdateFunction('faceCamera'));
elTextLineHeight.addEventListener('change', createEmitNumberPropertyUpdateFunction('lineHeight'));
-
var textTextColorChangeFunction = createEmitColorPropertyUpdateFunction(
'textColor', elTextTextColorRed, elTextTextColorGreen, elTextTextColorBlue);
elTextTextColorRed.addEventListener('change', textTextColorChangeFunction);
@@ -1707,6 +1709,10 @@
+
+
+
+
diff --git a/scripts/system/users.js b/scripts/system/users.js
index c010b7ea24..5b0ba42a45 100644
--- a/scripts/system/users.js
+++ b/scripts/system/users.js
@@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-var PopUpMenu = function (properties) {
+var PopUpMenu = function(properties) {
var value = properties.value,
promptOverlay,
valueOverlay,
@@ -217,7 +217,7 @@ var PopUpMenu = function (properties) {
};
};
-var usersWindow = (function () {
+var usersWindow = (function() {
var baseURL = Script.resolvePath("assets/images/tools/"),
WINDOW_WIDTH = 260,
@@ -253,7 +253,11 @@ var usersWindow = (function () {
WINDOW_BORDER_BOTTOM_MARGIN = WINDOW_BASE_MARGIN,
WINDOW_BORDER_LEFT_MARGIN = WINDOW_BASE_MARGIN,
WINDOW_BORDER_RADIUS = 4,
- WINDOW_BORDER_COLOR = { red: 255, green: 255, blue: 255 },
+ WINDOW_BORDER_COLOR = {
+ red: 255,
+ green: 255,
+ blue: 255
+ },
WINDOW_BORDER_ALPHA = 0.5,
windowBorder,
@@ -377,9 +381,12 @@ var usersWindow = (function () {
isMirrorDisplay = false,
isFullscreenMirror = false,
- windowPosition = { }, // Bottom left corner of window pane.
+ windowPosition = {}, // Bottom left corner of window pane.
isMovingWindow = false,
- movingClickOffset = { x: 0, y: 0 },
+ movingClickOffset = {
+ x: 0,
+ y: 0
+ },
isUsingScrollbars = false,
isMovingScrollbar = false,
@@ -401,9 +408,7 @@ var usersWindow = (function () {
}
// Reserve space for title, friends button, and option controls
- nonUsersHeight = WINDOW_MARGIN + windowLineHeight + FRIENDS_BUTTON_SPACER + FRIENDS_BUTTON_HEIGHT + DISPLAY_SPACER
- + windowLineHeight + VISIBILITY_SPACER
- + windowLineHeight + WINDOW_BASE_MARGIN;
+ nonUsersHeight = WINDOW_MARGIN + windowLineHeight + FRIENDS_BUTTON_SPACER + FRIENDS_BUTTON_HEIGHT + DISPLAY_SPACER + windowLineHeight + VISIBILITY_SPACER + windowLineHeight + WINDOW_BASE_MARGIN;
// Limit window to height of viewport above window position minus VU meter and mirror if displayed
windowHeight = linesOfUsers.length * windowLineHeight - windowLineSpacing + nonUsersHeight;
@@ -456,17 +461,14 @@ var usersWindow = (function () {
x: scrollbarBackgroundPosition.x,
y: scrollbarBackgroundPosition.y
});
- scrollbarBarPosition.y = scrollbarBackgroundPosition.y + 1
- + scrollbarValue * (scrollbarBackgroundHeight - scrollbarBarHeight - 2);
+ scrollbarBarPosition.y = scrollbarBackgroundPosition.y + 1 + scrollbarValue * (scrollbarBackgroundHeight - scrollbarBarHeight - 2);
Overlays.editOverlay(scrollbarBar, {
x: scrollbarBackgroundPosition.x + 1,
y: scrollbarBarPosition.y
});
x = windowLeft + WINDOW_MARGIN;
- y = windowPosition.y - FRIENDS_BUTTON_HEIGHT - DISPLAY_SPACER
- - windowLineHeight - VISIBILITY_SPACER
- - windowLineHeight - WINDOW_BASE_MARGIN;
+ y = windowPosition.y - FRIENDS_BUTTON_HEIGHT - DISPLAY_SPACER - windowLineHeight - VISIBILITY_SPACER - windowLineHeight - WINDOW_BASE_MARGIN;
Overlays.editOverlay(friendsButton, {
x: x,
y: y
@@ -554,9 +556,36 @@ var usersWindow = (function () {
usersRequest.ontimeout = pollUsersTimedOut;
usersRequest.onreadystatechange = processUsers;
usersRequest.send();
+ checkLoggedIn();
}
- processUsers = function () {
+ var loggedIn = false;
+
+ function checkLoggedIn() {
+ loggedIn = Account.isLoggedIn();
+ if (loggedIn === false) {
+ Overlays.editOverlay(friendsButton, {
+ visible: false
+ });
+ visibilityControl.setVisible(false);
+ displayControl.setVisible(false);
+ } else {
+ if (isMinimized === true) {
+ loggedIn = true;
+ return
+ }
+ Overlays.editOverlay(friendsButton, {
+ visible: true
+ });
+ visibilityControl.setVisible(true);
+ displayControl.setVisible(true);
+ loggedIn = true;
+
+ }
+ }
+
+
+ processUsers = function() {
var response,
myUsername,
user,
@@ -609,7 +638,7 @@ var usersWindow = (function () {
}
};
- pollUsersTimedOut = function () {
+ pollUsersTimedOut = function() {
print("Error: Request for users status timed out");
usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay.
};
@@ -633,11 +662,15 @@ var usersWindow = (function () {
Overlays.editOverlay(scrollbarBar, {
visible: isVisible && isUsingScrollbars && !isMinimized
});
- Overlays.editOverlay(friendsButton, {
- visible: isVisible && !isMinimized
- });
- displayControl.setVisible(isVisible && !isMinimized);
- visibilityControl.setVisible(isVisible && !isMinimized);
+
+ if (loggedIn === true) {
+ Overlays.editOverlay(friendsButton, {
+ visible: isVisible && !isMinimized
+ });
+ displayControl.setVisible(isVisible && !isMinimized);
+ visibilityControl.setVisible(isVisible && !isMinimized);
+ }
+
}
function setVisible(visible) {
@@ -730,9 +763,7 @@ var usersWindow = (function () {
userClicked = firstUserToDisplay + lineClicked;
- if (0 <= userClicked && userClicked < linesOfUsers.length && 0 <= overlayX
- && overlayX <= usersOnline[linesOfUsers[userClicked]].textWidth) {
- //print("Go to " + usersOnline[linesOfUsers[userClicked]].username);
+ if (0 <= userClicked && userClicked < linesOfUsers.length && 0 <= overlayX && overlayX <= usersOnline[linesOfUsers[userClicked]].textWidth) {
location.goToUser(usersOnline[linesOfUsers[userClicked]].username);
}
@@ -800,12 +831,8 @@ var usersWindow = (function () {
var isVisible;
if (isMovingScrollbar) {
- if (scrollbarBackgroundPosition.x - WINDOW_MARGIN <= event.x
- && event.x <= scrollbarBackgroundPosition.x + SCROLLBAR_BACKGROUND_WIDTH + WINDOW_MARGIN
- && scrollbarBackgroundPosition.y - WINDOW_MARGIN <= event.y
- && event.y <= scrollbarBackgroundPosition.y + scrollbarBackgroundHeight + WINDOW_MARGIN) {
- scrollbarValue = (event.y - scrollbarBarClickedAt * scrollbarBarHeight - scrollbarBackgroundPosition.y)
- / (scrollbarBackgroundHeight - scrollbarBarHeight - 2);
+ if (scrollbarBackgroundPosition.x - WINDOW_MARGIN <= event.x && event.x <= scrollbarBackgroundPosition.x + SCROLLBAR_BACKGROUND_WIDTH + WINDOW_MARGIN && scrollbarBackgroundPosition.y - WINDOW_MARGIN <= event.y && event.y <= scrollbarBackgroundPosition.y + scrollbarBackgroundHeight + WINDOW_MARGIN) {
+ scrollbarValue = (event.y - scrollbarBarClickedAt * scrollbarBarHeight - scrollbarBackgroundPosition.y) / (scrollbarBackgroundHeight - scrollbarBarHeight - 2);
scrollbarValue = Math.min(Math.max(scrollbarValue, 0.0), 1.0);
firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay));
updateOverlayPositions();
@@ -831,13 +858,9 @@ var usersWindow = (function () {
isVisible = isBorderVisible;
if (isVisible) {
- isVisible = windowPosition.x - WINDOW_BORDER_LEFT_MARGIN <= event.x
- && event.x <= windowPosition.x - WINDOW_BORDER_LEFT_MARGIN + WINDOW_BORDER_WIDTH
- && windowPosition.y - windowHeight - WINDOW_BORDER_TOP_MARGIN <= event.y
- && event.y <= windowPosition.y + WINDOW_BORDER_BOTTOM_MARGIN;
+ isVisible = windowPosition.x - WINDOW_BORDER_LEFT_MARGIN <= event.x && event.x <= windowPosition.x - WINDOW_BORDER_LEFT_MARGIN + WINDOW_BORDER_WIDTH && windowPosition.y - windowHeight - WINDOW_BORDER_TOP_MARGIN <= event.y && event.y <= windowPosition.y + WINDOW_BORDER_BOTTOM_MARGIN;
} else {
- isVisible = windowPosition.x <= event.x && event.x <= windowPosition.x + WINDOW_WIDTH
- && windowPosition.y - windowHeight <= event.y && event.y <= windowPosition.y;
+ isVisible = windowPosition.x <= event.x && event.x <= windowPosition.x + WINDOW_WIDTH && windowPosition.y - windowHeight <= event.y && event.y <= windowPosition.y;
}
if (isVisible !== isBorderVisible) {
isBorderVisible = isVisible;
@@ -878,8 +901,7 @@ var usersWindow = (function () {
isMirrorDisplay = Menu.isOptionChecked(MIRROR_MENU_ITEM);
isFullscreenMirror = Menu.isOptionChecked(FULLSCREEN_MIRROR_MENU_ITEM);
- if (viewport.y !== oldViewport.y || isMirrorDisplay !== oldIsMirrorDisplay
- || isFullscreenMirror !== oldIsFullscreenMirror) {
+ if (viewport.y !== oldViewport.y || isMirrorDisplay !== oldIsMirrorDisplay || isFullscreenMirror !== oldIsFullscreenMirror) {
calculateWindowHeight();
updateUsersDisplay();
}
@@ -929,8 +951,8 @@ var usersWindow = (function () {
} else {
hmdViewport = Controller.getRecommendedOverlayRect();
windowPosition = {
- x: (viewport.x - hmdViewport.width) / 2, // HMD viewport is narrower than screen.
- y: hmdViewport.height // HMD viewport starts at top of screen but only extends down so far.
+ x: (viewport.x - hmdViewport.width) / 2, // HMD viewport is narrower than screen.
+ y: hmdViewport.height // HMD viewport starts at top of screen but only extends down so far.
};
}
@@ -938,7 +960,7 @@ var usersWindow = (function () {
windowBorder = Overlays.addOverlay("rectangle", {
x: 0,
- y: viewport.y, // Start up off-screen
+ y: viewport.y, // Start up off-screen
width: WINDOW_BORDER_WIDTH,
height: windowBorderHeight,
radius: WINDOW_BORDER_RADIUS,
@@ -1101,6 +1123,11 @@ var usersWindow = (function () {
visible: isVisible && !isMinimized
});
+
+ Script.setTimeout(function() {
+ checkLoggedIn()
+ }, 0);
+
Controller.mousePressEvent.connect(onMousePressEvent);
Controller.mouseMoveEvent.connect(onMouseMoveEvent);
Controller.mouseReleaseEvent.connect(onMouseReleaseEvent);
@@ -1143,4 +1170,4 @@ var usersWindow = (function () {
setUp();
Script.scriptEnding.connect(tearDown);
-}());
+}());
\ No newline at end of file
diff --git a/scripts/tutorials/getDomainMetadata.js b/scripts/tutorials/getDomainMetadata.js
new file mode 100644
index 0000000000..54c356ae7b
--- /dev/null
+++ b/scripts/tutorials/getDomainMetadata.js
@@ -0,0 +1,129 @@
+//
+// Created by Zach Pomerantz on June 16, 2016.
+// 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
+//
+
+var SERVER = 'https://metaverse.highfidelity.com/api/v1';
+
+var OVERLAY = null;
+
+// Every time you enter a domain, display the domain's metadata
+location.hostChanged.connect(function(host) {
+ print('Detected host change:', host);
+
+ // Fetch the domain ID from the metaverse
+ var placeData = request(SERVER + '/places/' + host);
+ if (!placeData) {
+ print('Cannot find place name - abandoning metadata request for', host);
+ return;
+
+ }
+ var domainID = placeData.data.place.domain.id;
+ print('Domain ID:', domainID);
+
+ // Fetch the domain metadata from the metaverse
+ var domainData = request(SERVER + '/domains/' + domainID);
+ print(SERVER + '/domains/' + domainID);
+ if (!domainData) {
+ print('Cannot find domain data - abandoning metadata request for', domainID);
+ return;
+ }
+ var metadata = domainData.domain;
+ print('Domain metadata:', JSON.stringify(metadata));
+
+ // Display the fetched metadata in an overlay
+ displayMetadata(host, metadata);
+});
+
+Script.scriptEnding.connect(clearMetadata);
+
+function displayMetadata(place, metadata) {
+ clearMetadata();
+
+ var COLOR_TEXT = { red: 255, green: 255, blue: 255 };
+ var COLOR_BACKGROUND = { red: 0, green: 0, blue: 0 };
+ var MARGIN = 200;
+ var STARTING_OPACITY = 0.8;
+ var FADE_AFTER_SEC = 2;
+ var FADE_FOR_SEC = 4;
+
+ var fade_per_sec = STARTING_OPACITY / FADE_FOR_SEC;
+ var properties = {
+ color: COLOR_TEXT,
+ alpha: STARTING_OPACITY,
+ backgroundColor: COLOR_BACKGROUND,
+ backgroundAlpha: STARTING_OPACITY,
+ font: { size: 24 },
+ x: MARGIN,
+ y: MARGIN
+ };
+
+ // Center the overlay on the screen
+ properties.width = Window.innerWidth - MARGIN*2;
+ properties.height = Window.innerHeight - MARGIN*2;
+
+ // Parse the metadata into text
+ parsed = [ 'Welcome to ' + place + '!',, ];
+ if (metadata.description) {
+ parsed.push(description);
+ }
+ if (metadata.tags && metadata.tags.length) {
+ parsed.push('Tags: ' + metadata.tags.join(','));
+ }
+ if (metadata.capacity) {
+ parsed.push('Capacity (max users): ' + metadata.capacity);
+ }
+ if (metadata.maturity) {
+ parsed.push('Maturity: ' + metadata.maturity);
+ }
+ if (metadata.hosts && metadata.hosts.length) {
+ parsed.push('Hosts: ' + metadata.tags.join(','));
+ }
+ if (metadata.online_users) {
+ parsed.push('Users online: ' + metadata.online_users);
+ }
+
+ properties.text = parsed.join('\n\n');
+
+ // Display the overlay
+ OVERLAY = Overlays.addOverlay('text', properties);
+
+ // Fade out the overlay over 10 seconds
+ !function() {
+ var overlay = OVERLAY;
+ var alpha = STARTING_OPACITY;
+
+ var fade = function() {
+ // Only fade so long as the same overlay is up
+ if (overlay == OVERLAY) {
+ alpha -= fade_per_sec / 10;
+ if (alpha <= 0) {
+ clearMetadata();
+ } else {
+ Overlays.editOverlay(overlay, { alpha: alpha, backgroundAlpha: alpha });
+ Script.setTimeout(fade, 100);
+ }
+ }
+ };
+ Script.setTimeout(fade, FADE_AFTER_SEC * 1000);
+ }();
+}
+
+function clearMetadata() {
+ if (OVERLAY) {
+ Overlays.deleteOverlay(OVERLAY);
+ }
+}
+
+// Request JSON from a url, synchronously
+function request(url) {
+ var req = new XMLHttpRequest();
+ req.responseType = 'json';
+ req.open('GET', url, false);
+ req.send();
+ return req.status == 200 ? req.response : null;
+}
+
diff --git a/tests/controllers/CMakeLists.txt b/tests/controllers/CMakeLists.txt
index cf1152da02..3aac4db0a8 100644
--- a/tests/controllers/CMakeLists.txt
+++ b/tests/controllers/CMakeLists.txt
@@ -6,7 +6,7 @@ setup_hifi_project(Script Qml)
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
# link in the shared libraries
-link_hifi_libraries(shared gl script-engine plugins render-utils input-plugins display-plugins controllers)
+link_hifi_libraries(shared gl script-engine plugins render-utils ui-plugins input-plugins display-plugins controllers)
if (WIN32)
diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp
index 36ed566ea7..15b768bb36 100644
--- a/tests/controllers/src/main.cpp
+++ b/tests/controllers/src/main.cpp
@@ -34,7 +34,7 @@
#include
#include
-#include
+#include
#include
#include
#include
@@ -90,8 +90,8 @@ public:
virtual MainWindow* getPrimaryWindow() override { return nullptr; }
virtual QOpenGLContext* getPrimaryContext() override { return nullptr; }
virtual ui::Menu* getPrimaryMenu() override { return nullptr; }
- virtual bool isForeground() override { return true; }
- virtual const DisplayPluginPointer getActiveDisplayPlugin() const override { return DisplayPluginPointer(); }
+ virtual bool isForeground() const override { return true; }
+ virtual DisplayPluginPointer getActiveDisplayPlugin() const override { return DisplayPluginPointer(); }
};
class MyControllerScriptingInterface : public controller::ScriptingInterface {
diff --git a/tests/ui/qml/main.qml b/tests/ui/qml/main.qml
index 19f6a55bfd..47d0f6d601 100644
--- a/tests/ui/qml/main.qml
+++ b/tests/ui/qml/main.qml
@@ -57,7 +57,7 @@ ApplicationWindow {
"particle-01.svg",
]
property int iconIndex: 0
- readonly property string toolIconUrl: "file:///C:/Users/bdavi/git/hifi/scripts/system/assets/images/tools/"
+ readonly property string toolIconUrl: "../../../../../scripts/system/assets/images/tools/"
text: "Create Button"
onClicked: {
var name = icons[iconIndex];
|