| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "peridot/lib/session_shell_settings/session_shell_settings.h" |
| |
| #include <cmath> |
| |
| #include <lib/fxl/files/file.h> |
| #include <lib/fxl/logging.h> |
| |
| #include "rapidjson/document.h" |
| |
| namespace modular { |
| |
| namespace { |
| |
| constexpr char kBaseShellConfigJsonPath[] = |
| "/system/data/sysui/base_shell_config.json"; |
| std::vector<SessionShellSettings>* g_system_settings; |
| |
| } // namespace |
| |
| namespace internal { |
| |
| // Given a rapidjson::Value that is an object (i.e. a key-value map), look up |
| // |key| in the object and return the value associated with it. |
| template <typename T> |
| T GetObjectValue(const rapidjson::Value& object, const std::string& key); |
| |
| // Specializes |GetObjectValue|, expecting the type of object[key] to be an JSON |
| // string. Returns "" if the key doesn't exist or isn't a JSON string. |
| template <> |
| std::string GetObjectValue(const rapidjson::Value& object, |
| const std::string& key) { |
| if (!object.IsObject()) |
| return ""; |
| |
| if (!object.HasMember(key)) |
| return ""; |
| |
| const auto& field = object[key]; |
| |
| if (!field.IsString()) |
| return ""; |
| |
| return field.GetString(); |
| } |
| |
| // Specializes |GetObjectValue|, expecting the type of object[key] to be a _JSON |
| // string that represents a float_. (Note that the type is not expected to be a |
| // raw JSON number!). Returns std::numeric_limits<float>::signaling_NaN() if |
| // the float couldn't be parsed from the string. |
| template <> |
| float GetObjectValue(const rapidjson::Value& object, const std::string& key) { |
| auto str = GetObjectValue<std::string>(object, key); |
| |
| if (str.empty()) |
| return std::numeric_limits<float>::signaling_NaN(); |
| |
| const char* const c_str = str.c_str(); |
| char* c_str_end; |
| |
| const float f = std::strtof(c_str, &c_str_end); |
| if (f == HUGE_VAL || (f == 0.0f && c_str_end == c_str)) |
| return std::numeric_limits<float>::signaling_NaN(); |
| |
| return f; |
| } |
| |
| // Specializes |GetObjectValue|, expecting the type of object[key] to be a JSON |
| // string that represents a fuchsia::ui::policy::DisplayUsage. Returns kUnknown |
| // if the string couldn't be parsed. |
| template <> |
| fuchsia::ui::policy::DisplayUsage GetObjectValue(const rapidjson::Value& object, |
| const std::string& key) { |
| auto str = GetObjectValue<std::string>(object, key); |
| if (str.empty()) { |
| return fuchsia::ui::policy::DisplayUsage::kUnknown; |
| } |
| |
| // Keep in sync with |
| // <https://fuchsia.googlesource.com/topaz/+/master/lib/base_shell/lib/session_shell_chooser.dart#64>. |
| if (str == "handheld") { |
| return fuchsia::ui::policy::DisplayUsage::kHandheld; |
| } else if (str == "close") { |
| return fuchsia::ui::policy::DisplayUsage::kClose; |
| } else if (str == "near") { |
| return fuchsia::ui::policy::DisplayUsage::kNear; |
| } else if (str == "midrange") { |
| return fuchsia::ui::policy::DisplayUsage::kMidrange; |
| } else if (str == "far") { |
| return fuchsia::ui::policy::DisplayUsage::kFar; |
| } else { |
| FXL_LOG(WARNING) << "unknown display usage string: " << str; |
| return fuchsia::ui::policy::DisplayUsage::kUnknown; |
| } |
| }; |
| |
| // Given a |json| string, parses it into list of session shell settings. |
| std::vector<SessionShellSettings> ParseSessionShellSettings( |
| const std::string& json) { |
| std::vector<SessionShellSettings> settings; |
| |
| rapidjson::Document document; |
| document.Parse(json.c_str()); |
| if (document.HasParseError()) { |
| FXL_LOG(ERROR) << "ParseSessionShellSettings(): parse error " |
| << document.GetParseError(); |
| return settings; |
| } |
| |
| if (!document.IsArray()) { |
| FXL_LOG(ERROR) << "ParseSessionShellSettings(): root item isn't an array"; |
| return settings; |
| } |
| |
| if (document.Empty()) { |
| FXL_LOG(ERROR) << "ParseSessionShellSettings(): root array is empty"; |
| return settings; |
| } |
| |
| settings.reserve(document.Size()); |
| |
| for (rapidjson::SizeType i = 0; i < document.Size(); i++) { |
| using modular::internal::GetObjectValue; |
| |
| const auto& session_shell = document[i]; |
| |
| const auto& name = GetObjectValue<std::string>(session_shell, "name"); |
| if (name.empty()) |
| continue; |
| |
| settings.push_back({ |
| .name = GetObjectValue<std::string>(session_shell, "name"), |
| .screen_width = GetObjectValue<float>(session_shell, "screen_width"), |
| .screen_height = GetObjectValue<float>(session_shell, "screen_height"), |
| .display_usage = GetObjectValue<fuchsia::ui::policy::DisplayUsage>( |
| session_shell, "display_usage"), |
| .auto_login = |
| GetObjectValue<std::string>(session_shell, "auto_login") == "true", |
| }); |
| } |
| |
| return settings; |
| } |
| |
| } // namespace internal |
| |
| const std::vector<SessionShellSettings>& |
| SessionShellSettings::GetSystemSettings() { |
| // This method is intentionally thread-hostile to keep things simple, and |
| // needs modification to be thread-safe or thread-compatible. |
| |
| if (g_system_settings) { |
| return *g_system_settings; |
| } |
| |
| g_system_settings = new std::vector<SessionShellSettings>; |
| |
| std::string json; |
| if (!files::ReadFileToString(kBaseShellConfigJsonPath, &json)) { |
| FXL_LOG(ERROR) << kBaseShellConfigJsonPath << ": read failed"; |
| |
| return *g_system_settings; |
| } |
| |
| *g_system_settings = internal::ParseSessionShellSettings(json); |
| return *g_system_settings; |
| } |
| |
| bool operator==(const SessionShellSettings& lhs, |
| const SessionShellSettings& rhs) { |
| auto float_eq = [](float f, float g) { |
| // OK to do direct float comparison here, since we want bitwise value |
| // equality. |
| if (f == g) |
| return true; |
| |
| if (std::isnan(f) && std::isnan(g)) |
| return true; |
| |
| return false; |
| }; |
| |
| return lhs.name == rhs.name && lhs.auto_login == rhs.auto_login && |
| float_eq(lhs.screen_width, rhs.screen_width) && |
| float_eq(lhs.screen_height, rhs.screen_height) && |
| lhs.display_usage == rhs.display_usage; |
| } |
| |
| } // namespace modular |