Feature/webgui ops (#723)

* Update node packages, download nodejs in build process, start server from openspace process
  * Patch CEF cmake automatically
  * Build webserver automatically
  * Work on CMake for WebBrowser, Webgui and CefWebGui
  * Map key modifiers to CEF
  * Smooth time interpolation in webgui
  * Automatically focus on search field in filter lists
  * Move webgui code to external repositories
  * Use asset system to distribute webgui
  * Remove webgui from main repository
  * Add support for right click in webgui and improve timetopic
  * Resolve cmake policy warning
  * Add relative time interpolation to lua interface
  * Sanitize json error message before logging error. Workaround for #736
  * Added gui properties to scene graph nodes
  * Add version topic
  * Add shortcuttopic
  * Add ability to disable rendering of cefwebgui
  * Don't do message loop work if there is no browser.
  * Set correct path to nodejs on unix
  * Message loop work in presync
  * modifications for shortcuts in gui, added names for shortcuts
  * Set properties via lua scripts
  * Allow gui grouping for shortcuts
  * Add gui paths keybindings
  * Blocking keyboard callbacks when webgui has keyboard focus in an editable field
  * Allow disabling of WebBrowser and CefWebGui
  * Make it possible to hide GUI
  * Get rid of redundant dashboard items if web gui is used
  * Hide WebGUI on slave nodes
  * Hide WebGUI on main rendering window if a GUI window exists
 * Enable WebGUI on default unless it is overwritten in the openspace.cfg
  * Add guiName for propery owners in socket api
This commit is contained in:
Emil Axelsson
2018-11-05 20:45:38 -05:00
committed by Alexander Bock
parent ec67169854
commit 7181de4673
286 changed files with 2546 additions and 18165 deletions
+18 -4
View File
@@ -29,10 +29,12 @@
#include <modules/server/include/topics/getpropertytopic.h>
#include <modules/server/include/topics/luascripttopic.h>
#include <modules/server/include/topics/setpropertytopic.h>
#include <modules/server/include/topics/shortcuttopic.h>
#include <modules/server/include/topics/subscriptiontopic.h>
#include <modules/server/include/topics/timetopic.h>
#include <modules/server/include/topics/topic.h>
#include <modules/server/include/topics/triggerpropertytopic.h>
#include <modules/server/include/topics/versiontopic.h>
#include <openspace/engine/configuration.h>
#include <openspace/engine/globals.h>
#include <ghoul/io/socket/socket.h>
@@ -48,10 +50,12 @@ namespace {
constexpr const char* MessageKeyPayload = "payload";
constexpr const char* MessageKeyTopic = "topic";
constexpr const char* VersionTopicKey = "version";
constexpr const char* AuthenticationTopicKey = "authorize";
constexpr const char* GetPropertyTopicKey = "get";
constexpr const char* LuaScriptTopicKey = "luascript";
constexpr const char* SetPropertyTopicKey = "set";
constexpr const char* ShortcutTopicKey = "shortcuts";
constexpr const char* SubscriptionTopicKey = "subscribe";
constexpr const char* TimeTopicKey = "time";
constexpr const char* TriggerPropertyTopicKey = "trigger";
@@ -70,10 +74,12 @@ Connection::Connection(std::unique_ptr<ghoul::io::Socket> s, std::string address
_topicFactory.registerClass<GetPropertyTopic>(GetPropertyTopicKey);
_topicFactory.registerClass<LuaScriptTopic>(LuaScriptTopicKey);
_topicFactory.registerClass<SetPropertyTopic>(SetPropertyTopicKey);
_topicFactory.registerClass<ShortcutTopic>(ShortcutTopicKey);
_topicFactory.registerClass<SubscriptionTopic>(SubscriptionTopicKey);
_topicFactory.registerClass<TimeTopic>(TimeTopicKey);
_topicFactory.registerClass<TriggerPropertyTopic>(TriggerPropertyTopicKey);
_topicFactory.registerClass<BounceTopic>(BounceTopicKey);
_topicFactory.registerClass<VersionTopic>(VersionTopicKey);
// see if the default config for requiring auth (on) is overwritten
_requireAuthorization = global::configuration.doesRequireSocketAuthentication;
@@ -84,9 +90,8 @@ void Connection::handleMessage(const std::string& message) {
nlohmann::json j = nlohmann::json::parse(message.c_str());
try {
handleJson(j);
}
catch (...) {
LERROR(fmt::format("JSON handling error from: {}", message));
} catch (const std::exception& e) {
LERROR(fmt::format("JSON handling error from: {}. {}", message, e.what()));
}
} catch (...) {
if (!isAuthorized()) {
@@ -97,7 +102,16 @@ void Connection::handleMessage(const std::string& message) {
));
return;
} else {
LERROR(fmt::format("Could not parse JSON: '{}'", message));
std::string sanitizedString = message;
std::transform(
message.begin(),
message.end(),
sanitizedString.begin(),
[](const unsigned char& c) {
return std::isprint(c) ? c : ' ';
}
);
LERROR(fmt::format("Could not parse JSON: '{}'", sanitizedString));
}
}
}
+2 -2
View File
@@ -24,7 +24,6 @@
#include <modules/server/include/jsonconverters.h>
#include <openspace/json.h>
#include <openspace/properties/property.h>
#include <openspace/rendering/renderable.h>
#include <openspace/scene/scenegraphnode.h>
@@ -37,7 +36,7 @@ namespace openspace::properties {
void to_json(json& j, const Property& p) {
j = {
{ "Description", json::parse(p.generateBaseJsonDescription()) },
{ "Value", p.jsonValue() }
{ "Value", json::parse(p.jsonValue()) }
};
j["Description"]["description"] = p.description();
}
@@ -49,6 +48,7 @@ void to_json(json& j, const Property* pP) {
void to_json(json& j, const PropertyOwner& p) {
j = {
{ "identifier", p.identifier() },
{ "guiName", p.guiName() },
{ "description", p.description() },
{ "properties", p.properties() },
{ "subowners", p.propertySubOwners() },
@@ -39,7 +39,6 @@ namespace openspace {
void LuaScriptTopic::handleJson(const nlohmann::json& json) {
try {
std::string script = json.at(ScriptKey).get<std::string>();
LDEBUG("Queueing Lua script: " + script);
global::scriptEngine.queueScript(
std::move(script),
scripting::ScriptEngine::RemoteScripting::No
+54 -10
View File
@@ -26,7 +26,7 @@
#include <openspace/json.h>
#include <openspace/engine/globals.h>
#include <openspace/properties/property.h>
#include <openspace/scripting/scriptengine.h>
#include <openspace/query/query.h>
#include <openspace/util/timemanager.h>
#include <openspace/util/time.h>
@@ -37,6 +37,48 @@ namespace {
constexpr const char* ValueKey = "value";
constexpr const char* _loggerCat = "SetPropertyTopic";
constexpr const char* SpecialKeyTime = "__time";
std::string escapedLuaString(const std::string& str) {
std::string luaString;
for (const char& c : str) {
switch (c) {
case '\'':
luaString += "\'";
break;
default:
luaString += c;
}
}
return luaString;
}
std::string luaLiteralFromJson(nlohmann::json value) {
if (value.is_string()) {
return "'" + escapedLuaString(value.get<std::string>()) + "'";
} else if (value.is_boolean()) {
return value.get<bool>() ? "true" : "false";
} else if (value.is_number()) {
return std::to_string(value.get<double>());
} else if (value.is_array()) {
std::string literal = "{";
for (nlohmann::json::iterator it = value.begin(); it != value.end(); ++it) {
literal += luaLiteralFromJson(it.value()) += ",";
}
literal.pop_back(); // remove last comma
literal += "}";
return literal;
} else if (value.is_object()) {
std::string literal = "{";
for (nlohmann::json::iterator it = value.begin(); it != value.end(); ++it) {
literal += it.key() + "=" + luaLiteralFromJson(it.value()) += ",";
}
literal.pop_back(); // remove last comma
literal += "}";
return literal;
}{
return "null";
}
}
} // namespace
namespace openspace {
@@ -44,21 +86,20 @@ namespace openspace {
void SetPropertyTopic::handleJson(const nlohmann::json& json) {
try {
const std::string& propertyKey = json.at(PropertyKey).get<std::string>();
std::string value = json.at(ValueKey).get<std::string>();
if (propertyKey == SpecialKeyTime) {
Time newTime;
newTime.setTime(value);
newTime.setTime(json.at(ValueKey).get<std::string>());
global::timeManager.setTimeNextFrame(newTime);
}
else {
properties::Property* prop = property(propertyKey);
if (prop) {
LDEBUG("Setting " + propertyKey + " to " + value + ".");
if (!prop->setStringValue(std::move(value))) {
LERROR("Failed!");
}
}
nlohmann::json value = json.at(ValueKey);
std::string literal = luaLiteralFromJson(value);
global::scriptEngine.queueScript(
"openspace.setPropertyValueSingle(\"" + propertyKey + "\", " + literal + ")",
scripting::ScriptEngine::RemoteScripting::Yes
);
}
}
catch (const std::out_of_range& e) {
@@ -69,6 +110,9 @@ void SetPropertyTopic::handleJson(const nlohmann::json& json) {
LERROR("Could not set property -- runtime error:");
LERROR(e.what());
}
catch (...) {
LERROR("Could not set property -- unknown error");
}
}
bool SetPropertyTopic::isDone() const {
+122
View File
@@ -0,0 +1,122 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/server/include/topics/shortcuttopic.h>
#include <modules/server/include/connection.h>
#include <modules/server/servermodule.h>
#include <openspace/openspace.h>
#include <openspace/engine/globals.h>
#include <openspace/interaction/shortcutmanager.h>
#include <openspace/interaction/keybindingmanager.h>
namespace {
constexpr const char* EventKey = "event";
constexpr const char* StartSubscription = "start_subscription";
constexpr const char* StopSubscription = "stop_subscription";
} // namespace
using nlohmann::json;
namespace openspace {
ShortcutTopic::ShortcutTopic() {}
ShortcutTopic::~ShortcutTopic() {}
bool ShortcutTopic::isDone() const {
return true;
}
std::vector<nlohmann::json> ShortcutTopic::shortcutsJson() const {
using ShortcutInformation = interaction::ShortcutManager::ShortcutInformation;
const std::vector<ShortcutInformation>& shortcuts =
global::shortcutManager.shortcuts();
std::vector<nlohmann::json> json;
for (const ShortcutInformation& shortcut : shortcuts) {
nlohmann::json shortcutJson = {
{ "name", shortcut.name },
{ "script", shortcut.script },
{ "synchronization", static_cast<bool>(shortcut.synchronization) },
{ "documentation", shortcut.documentation },
{ "guiPath", shortcut.guiPath },
};
json.push_back(shortcutJson);
}
using KeyInformation = interaction::KeybindingManager::KeyInformation;
const std::multimap<KeyWithModifier, KeyInformation>& keyBindings =
global::keybindingManager.keyBindings();
for (const std::pair<KeyWithModifier, KeyInformation>& keyBinding : keyBindings) {
const KeyWithModifier& k = keyBinding.first;
const KeyInformation& info = keyBinding.second;
nlohmann::json shortcutJson = {
{ "key", ghoul::to_string(k.key) },
{ "modifiers",
{
{"shift" , hasKeyModifier(k.modifier, KeyModifier::Shift) },
{"control" , hasKeyModifier(k.modifier, KeyModifier::Control) },
{"alt" , hasKeyModifier(k.modifier, KeyModifier::Alt) },
{"super" , hasKeyModifier(k.modifier, KeyModifier::Super) }
}
},
{ "name", info.name },
{ "script", info.command },
{ "synchronization", static_cast<bool>(info.synchronization) },
{ "documentation", info.documentation },
{ "guiPath", info.guiPath },
};
json.push_back(shortcutJson);
}
return json;
}
void ShortcutTopic::sendData() const {
nlohmann::json data = {
{"shortcuts", shortcutsJson()}
};
_connection->sendJson(wrappedPayload(data));
}
void ShortcutTopic::handleJson(const nlohmann::json& input) {
const std::string& event = input.at(EventKey).get<std::string>();
if (event == StartSubscription) {
// TODO: Subscribe to shortcuts and keybindings
// shortcutManager.subscribe(); ...
} else if (event == StopSubscription) {
// TODO: Unsubscribe to shortcuts and keybindings
// shortcutManager.unsubscribe(); ...
return;
}
sendData();
}
} // namespace openspace
@@ -63,14 +63,11 @@ void SubscriptionTopic::handleJson(const nlohmann::json& json) {
const std::string& event = json.at(EventKey).get<std::string>();
if (event == StartSubscription) {
LDEBUG(fmt::format("Subscribing to property '{}'", key));
_prop = property(key);
if (_prop) {
_requestedResourceIsSubscribable = true;
_isSubscribedTo = true;
auto onChange = [this, k = std::move(key)]() {
LDEBUG("Updating subscription '" + k + "'.");
_connection->sendJson(wrappedPayload(_prop));
};
_onChangeHandle = _prop->onChange(onChange);
+15 -6
View File
@@ -38,7 +38,7 @@ namespace {
constexpr const char* UnsubscribeEvent = "stop_subscription";
constexpr const char* CurrentTimeKey = "currentTime";
constexpr const char* DeltaTimeKey = "deltaTime";
constexpr const std::chrono::milliseconds TimeUpdateInterval(100);
constexpr const std::chrono::milliseconds TimeUpdateInterval(50);
} // namespace
using nlohmann::json;
@@ -87,10 +87,15 @@ void TimeTopic::handleJson(const nlohmann::json& json) {
else if (requestedKey == DeltaTimeKey) {
_deltaTimeCallbackHandle = global::timeManager.addDeltaTimeChangeCallback(
[this]() {
_connection->sendJson(deltaTime());
if (_timeCallbackHandle != UnsetOnChangeHandle) {
_connection->sendJson(currentTime());
_lastUpdateTime = std::chrono::system_clock::now();;
std::chrono::system_clock::time_point now =
std::chrono::system_clock::now();
if (now - _lastUpdateTime > TimeUpdateInterval) {
_connection->sendJson(deltaTime());
if (_timeCallbackHandle != UnsetOnChangeHandle) {
_connection->sendJson(currentTime());
_lastUpdateTime = std::chrono::system_clock::now();
}
_lastUpdateTime = now;
}
}
);
@@ -108,7 +113,11 @@ json TimeTopic::currentTime() {
}
json TimeTopic::deltaTime() {
json timeJson = { { "deltaTime", global::timeManager.deltaTime() } };
json timeJson = {
{ "deltaTime", global::timeManager.deltaTime() },
{ "targetDeltaTime", global::timeManager.targetDeltaTime() },
{ "isPaused", global::timeManager.isPaused() },
};
return wrappedPayload(timeJson);
}
@@ -0,0 +1,76 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "modules/server/include/topics/versiontopic.h"
#include <modules/server/include/connection.h>
#include <modules/server/servermodule.h>
#include <openspace/openspace.h>
#include <openspace/engine/globals.h>
namespace {
constexpr const char* _loggerCat = "VersionTopic";
} // namespace
using nlohmann::json;
namespace openspace {
VersionTopic::VersionTopic() {
}
VersionTopic::~VersionTopic() {
}
bool VersionTopic::isDone() const {
return true;
}
void VersionTopic::handleJson(const nlohmann::json&) {
nlohmann::json versionJson = {
{
"openSpaceVersion",
{
{"major", OPENSPACE_VERSION_MAJOR},
{"minor", OPENSPACE_VERSION_MINOR},
{"patch", OPENSPACE_VERSION_PATCH}
}
},
{
"socketApiVersion",
{
{"major", SOCKET_API_VERSION_MAJOR},
{"minor", SOCKET_API_VERSION_MINOR},
{"patch", SOCKET_API_VERSION_PATCH}
}
}
};
_connection->sendJson(
wrappedPayload(versionJson)
);
}
} // namespace openspace