diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | .gitreview | 5 | ||||
-rw-r--r-- | CMakeLists.txt | 72 | ||||
-rw-r--r-- | README.txt | 44 | ||||
-rw-r--r-- | include/cpptoml/LICENSE | 18 | ||||
-rw-r--r-- | include/cpptoml/cpptoml.h | 3274 | ||||
-rwxr-xr-x | package/hvac/bin/runxdg | 2 | ||||
-rw-r--r-- | package/hvac/config.xml | 17 | ||||
-rw-r--r-- | package/hvac/icon.svg | 279 | ||||
-rw-r--r-- | package/hvac/runxdg.toml | 22 | ||||
-rwxr-xr-x | package/navi/bin/runxdg | 2 | ||||
-rw-r--r-- | package/navi/config.xml | 17 | ||||
-rw-r--r-- | package/navi/icon.svg | 279 | ||||
-rw-r--r-- | package/navi/runxdg.toml | 22 | ||||
-rwxr-xr-x | package/simple-egl/bin/runxdg | 2 | ||||
-rw-r--r-- | package/simple-egl/config.xml | 17 | ||||
-rw-r--r-- | package/simple-egl/icon.svg | 279 | ||||
-rw-r--r-- | package/simple-egl/runxdg.toml | 22 | ||||
-rw-r--r-- | src/runxdg.cpp | 621 | ||||
-rw-r--r-- | src/runxdg.hpp | 164 |
20 files changed, 5159 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..c22e5d1 --- /dev/null +++ b/.gitreview @@ -0,0 +1,5 @@ +[gerrit] +host=gerrit.automotivelinux.org +port=29418 +project=apps/app-templates +defaultbranch=master diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..196e7f5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,72 @@ +project (runxdg) + +cmake_minimum_required(VERSION 3.0) + +set(PROJECT_NAME "runxdg") +set(PROJECT_PRETTY_NAME "Run XDG Application") +set(PROJECT_MAJOR_VERSION "1.0") +set(PROJECT_MINOR_VERSION "1") +set(PROJECT_VERSION "${PROJECT_MAJOR_VERSION}.${PROJECT_MINOR_VERSION}") + +add_definitions(-DTARGET_APP_ID=${TARGET_APP_ID}) +add_definitions(-DRUNXDG_NAME=${RUNXDG_NAME}) + +set(CMAKE_CXX_FLAGS "-Wall -fpermissive") + +find_package(PkgConfig REQUIRED) +pkg_check_modules(GLIB REQUIRED glib-2.0) +pkg_check_modules(GIO REQUIRED gio-2.0) +pkg_check_modules(GMODULE REQUIRED gmodule-2.0) + +INCLUDE(FindPkgConfig) + +INCLUDE_DIRECTORIES( + include + ${GLIB_INCLUDE_DIRS} + ${GIO_INCLUDE_DIRS} + ) + +LINK_DIRECTORIES( + ${GLIB_LIBRARY_DIRS} + ${GIO_LIBRARY_DIRS} + ) + +SET(LIBRARIES + libwindowmanager.so + libhomescreen.so + libilmControl.so + libilmCommon.so + libafbwsc.so + libjson-c.so + libEGL.so + libGLESv2.so + libwayland-egl.so + libwayland-client.so + libpthread.so + ${GLIB_LIBRARIES} + ${GIO_LIBRARIES} +) + +SET(SRC_FILES + src/runxdg.cpp +) + +add_executable(${PROJECT_NAME} ${SRC_FILES}) + +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LIBRARIES}) + +add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND cp -rf ${CMAKE_CURRENT_SOURCE_DIR}/package ${PROJECT_BINARY_DIR}) + +add_custom_target(widget + + COMMAND wgtpkg-pack -f -o ${PROJECT_BINARY_DIR}/package/simple-egl.wgt ${PROJECT_BINARY_DIR}/package/simple-egl + + # override hvac to test runxdg + COMMAND wgtpkg-pack -f -o ${PROJECT_BINARY_DIR}/package/hvac.wgt ${PROJECT_BINARY_DIR}/package/hvac + + # override navigation to test runxdg + COMMAND wgtpkg-pack -f -o ${PROJECT_BINARY_DIR}/package/navi.wgt ${PROJECT_BINARY_DIR}/package/navi +) + +install (TARGETS ${PROJECT_NAME} DESTINATION bin) diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..5006c2d --- /dev/null +++ b/README.txt @@ -0,0 +1,44 @@ +How to build with SDK + +1. make build directory + $ mkdir build + +2. do cmake + $ cd build + $ cmake .. + + You could find binary executable "runxdg" is built. + "runxdg" should be installed into the directory + on the target. (e.g. /usr/bin or /usr/local/bin) + +3. Prepare configuration for RunXDG, 'runxdg.toml' + See 'package/{runxdg|navi|hvac}/runxdg.toml for detail. + + 'role', 'method', 'path' is mondatory. + + Only "POSIX" as 'method' works, others not tested so far. + + 'role' should be the same which defined in layers.json of WindowManager. + + e.g. followings are predefined role by default + "role": "MediaPlayer|Radio|Phone|Navigation|HVAC|Settings|Dashboard|POI|Mixer" + +3. Prepare config.xml for widget + + <content> should be follow. + <content src="bin/runxdg" type="application/vnd.agl.native"/> + + following <feature> is mandatory. + <feature name="urn:AGL:widget:required-api"> + <param name="homescreen" value="ws" /> + <param name="windowmanager" value="ws" /> + </feature> + +4. Make widgets + $ make widget + + The following wgt would be made. + - runxdg.wgt XDG Launcher + - navi.wgt for test, XDG Launcher installed as Navigation + - hvac.wgt for test, XDG Lanncher installed as HVAC + diff --git a/include/cpptoml/LICENSE b/include/cpptoml/LICENSE new file mode 100644 index 0000000..8802c4f --- /dev/null +++ b/include/cpptoml/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2014 Chase Geigle + +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. diff --git a/include/cpptoml/cpptoml.h b/include/cpptoml/cpptoml.h new file mode 100644 index 0000000..95c3820 --- /dev/null +++ b/include/cpptoml/cpptoml.h @@ -0,0 +1,3274 @@ +/** + * @file cpptoml.h + * @author Chase Geigle + * @date May 2013 + */ + +#ifndef _CPPTOML_H_ +#define _CPPTOML_H_ + +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <fstream> +#include <iomanip> +#include <map> +#include <memory> +#include <sstream> +#include <stdexcept> +#include <string> +#include <unordered_map> +#include <vector> + +#if __cplusplus > 201103L +#define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]] +#elif defined(__clang__) +#define CPPTOML_DEPRECATED(reason) __attribute__((deprecated(reason))) +#elif defined(__GNUG__) +#define CPPTOML_DEPRECATED(reason) __attribute__((deprecated)) +#elif defined(_MSC_VER) +#if _MSC_VER < 1910 +#define CPPTOML_DEPRECATED(reason) __declspec(deprecated) +#else +#define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]] +#endif +#endif + +namespace cpptoml +{ +class writer; // forward declaration +class base; // forward declaration +#if defined(CPPTOML_USE_MAP) +// a std::map will ensure that entries a sorted, albeit at a slight +// performance penalty relative to the (default) unordered_map +using string_to_base_map = std::map<std::string, std::shared_ptr<base>>; +#else +// by default an unordered_map is used for best performance as the +// toml specification does not require entries to be sorted +using string_to_base_map + = std::unordered_map<std::string, std::shared_ptr<base>>; +#endif + +template <class T> +class option +{ + public: + option() : empty_{true} + { + // nothing + } + + option(T value) : empty_{false}, value_{std::move(value)} + { + // nothing + } + + explicit operator bool() const + { + return !empty_; + } + + const T& operator*() const + { + return value_; + } + + const T* operator->() const + { + return &value_; + } + + const T& value_or(const T& alternative) const + { + if (!empty_) + return value_; + return alternative; + } + + private: + bool empty_; + T value_; +}; + +struct local_date +{ + int year = 0; + int month = 0; + int day = 0; +}; + +struct local_time +{ + int hour = 0; + int minute = 0; + int second = 0; + int microsecond = 0; +}; + +struct zone_offset +{ + int hour_offset = 0; + int minute_offset = 0; +}; + +struct local_datetime : local_date, local_time +{ +}; + +struct offset_datetime : local_datetime, zone_offset +{ + static inline struct offset_datetime from_zoned(const struct tm& t) + { + offset_datetime dt; + dt.year = t.tm_year + 1900; + dt.month = t.tm_mon + 1; + dt.day = t.tm_mday; + dt.hour = t.tm_hour; + dt.minute = t.tm_min; + dt.second = t.tm_sec; + + char buf[16]; + strftime(buf, 16, "%z", &t); + + int offset = std::stoi(buf); + dt.hour_offset = offset / 100; + dt.minute_offset = offset % 100; + return dt; + } + + CPPTOML_DEPRECATED("from_local has been renamed to from_zoned") + static inline struct offset_datetime from_local(const struct tm& t) + { + return from_zoned(t); + } + + static inline struct offset_datetime from_utc(const struct tm& t) + { + offset_datetime dt; + dt.year = t.tm_year + 1900; + dt.month = t.tm_mon + 1; + dt.day = t.tm_mday; + dt.hour = t.tm_hour; + dt.minute = t.tm_min; + dt.second = t.tm_sec; + return dt; + } +}; + +CPPTOML_DEPRECATED("datetime has been renamed to offset_datetime") +typedef offset_datetime datetime; + +class fill_guard +{ + public: + fill_guard(std::ostream& os) : os_(os), fill_{os.fill()} + { + // nothing + } + + ~fill_guard() + { + os_.fill(fill_); + } + + private: + std::ostream& os_; + std::ostream::char_type fill_; +}; + +inline std::ostream& operator<<(std::ostream& os, const local_date& dt) +{ + fill_guard g{os}; + os.fill('0'); + + using std::setw; + os << setw(4) << dt.year << "-" << setw(2) << dt.month << "-" << setw(2) + << dt.day; + + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const local_time& ltime) +{ + fill_guard g{os}; + os.fill('0'); + + using std::setw; + os << setw(2) << ltime.hour << ":" << setw(2) << ltime.minute << ":" + << setw(2) << ltime.second; + + if (ltime.microsecond > 0) + { + os << "."; + int power = 100000; + for (int curr_us = ltime.microsecond; curr_us; power /= 10) + { + auto num = curr_us / power; + os << num; + curr_us -= num * power; + } + } + + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const zone_offset& zo) +{ + fill_guard g{os}; + os.fill('0'); + + using std::setw; + + if (zo.hour_offset != 0 || zo.minute_offset != 0) + { + if (zo.hour_offset > 0) + { + os << "+"; + } + else + { + os << "-"; + } + os << setw(2) << std::abs(zo.hour_offset) << ":" << setw(2) + << std::abs(zo.minute_offset); + } + else + { + os << "Z"; + } + + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const local_datetime& dt) +{ + return os << static_cast<const local_date&>(dt) << "T" + << static_cast<const local_time&>(dt); +} + +inline std::ostream& operator<<(std::ostream& os, const offset_datetime& dt) +{ + return os << static_cast<const local_datetime&>(dt) + << static_cast<const zone_offset&>(dt); +} + +template <class T, class... Ts> +struct is_one_of; + +template <class T, class V> +struct is_one_of<T, V> : std::is_same<T, V> +{ +}; + +template <class T, class V, class... Ts> +struct is_one_of<T, V, Ts...> +{ + const static bool value + = std::is_same<T, V>::value || is_one_of<T, Ts...>::value; +}; + +template <class T> +class value; + +template <class T> +struct valid_value + : is_one_of<T, std::string, int64_t, double, bool, local_date, local_time, + local_datetime, offset_datetime> +{ +}; + +template <class T, class Enable = void> +struct value_traits; + +template <class T> +struct valid_value_or_string_convertible +{ + + const static bool value = valid_value<typename std::decay<T>::type>::value + || std::is_convertible<T, std::string>::value; +}; + +template <class T> +struct value_traits<T, typename std:: + enable_if<valid_value_or_string_convertible<T>:: + value>::type> +{ + using value_type = typename std:: + conditional<valid_value<typename std::decay<T>::type>::value, + typename std::decay<T>::type, std::string>::type; + + using type = value<value_type>; + + static value_type construct(T&& val) + { + return value_type(val); + } +}; + +template <class T> +struct value_traits<T, + typename std:: + enable_if<!valid_value_or_string_convertible<T>::value + && std::is_floating_point< + typename std::decay<T>::type>::value>:: + type> +{ + using value_type = typename std::decay<T>::type; + + using type = value<double>; + + static value_type construct(T&& val) + { + return value_type(val); + } +}; + +template <class T> +struct value_traits<T, + typename std:: + enable_if<!valid_value_or_string_convertible<T>::value + && std::is_signed<typename std::decay<T>:: + type>::value>::type> +{ + using value_type = int64_t; + + using type = value<int64_t>; + + static value_type construct(T&& val) + { + if (val < std::numeric_limits<int64_t>::min()) + throw std::underflow_error{"constructed value cannot be " + "represented by a 64-bit signed " + "integer"}; + + if (val > std::numeric_limits<int64_t>::max()) + throw std::overflow_error{"constructed value cannot be represented " + "by a 64-bit signed integer"}; + + return static_cast<int64_t>(val); + } +}; + +template <class T> +struct value_traits<T, + typename std:: + enable_if<!valid_value_or_string_convertible<T>::value + && std::is_unsigned<typename std::decay<T>:: + type>::value>::type> +{ + using value_type = int64_t; + + using type = value<int64_t>; + + static value_type construct(T&& val) + { + if (val > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) + throw std::overflow_error{"constructed value cannot be represented " + "by a 64-bit signed integer"}; + + return static_cast<int64_t>(val); + } +}; + +class array; +class table; +class table_array; + +template <class T> +struct array_of_trait +{ + using return_type = option<std::vector<T>>; +}; + +template <> +struct array_of_trait<array> +{ + using return_type = option<std::vector<std::shared_ptr<array>>>; +}; + +template <class T> +inline std::shared_ptr<typename value_traits<T>::type> make_value(T&& val); +inline std::shared_ptr<array> make_array(); +template <class T> +inline std::shared_ptr<T> make_element(); +inline std::shared_ptr<table> make_table(); +inline std::shared_ptr<table_array> make_table_array(); + +/** + * A generic base TOML value used for type erasure. + */ +class base : public std::enable_shared_from_this<base> +{ + public: + virtual ~base() = default; + + virtual std::shared_ptr<base> clone() const = 0; + + /** + * Determines if the given TOML element is a value. + */ + virtual bool is_value() const + { + return false; + } + + /** + * Determines if the given TOML element is a table. + */ + virtual bool is_table() const + { + return false; + } + + /** + * Converts the TOML element into a table. + */ + std::shared_ptr<table> as_table() + { + if (is_table()) + return std::static_pointer_cast<table>(shared_from_this()); + return nullptr; + } + /** + * Determines if the TOML element is an array of "leaf" elements. + */ + virtual bool is_array() const + { + return false; + } + + /** + * Converts the TOML element to an array. + */ + std::shared_ptr<array> as_array() + { + if (is_array()) + return std::static_pointer_cast<array>(shared_from_this()); + return nullptr; + } + + /** + * Determines if the given TOML element is an array of tables. + */ + virtual bool is_table_array() const + { + return false; + } + + /** + * Converts the TOML element into a table array. + */ + std::shared_ptr<table_array> as_table_array() + { + if (is_table_array()) + return std::static_pointer_cast<table_array>(shared_from_this()); + return nullptr; + } + + /** + * Attempts to coerce the TOML element into a concrete TOML value + * of type T. + */ + template <class T> + std::shared_ptr<value<T>> as(); + + template <class T> + std::shared_ptr<const value<T>> as() const; + + template <class Visitor, class... Args> + void accept(Visitor&& visitor, Args&&... args) const; + + protected: + base() + { + // nothing + } +}; + +/** + * A concrete TOML value representing the "leaves" of the "tree". + */ +template <class T> +class value : public base +{ + struct make_shared_enabler + { + // nothing; this is a private key accessible only to friends + }; + + template <class U> + friend std::shared_ptr<typename value_traits<U>::type> + cpptoml::make_value(U&& val); + + public: + static_assert(valid_value<T>::value, "invalid value type"); + + std::shared_ptr<base> clone() const override; + + value(const make_shared_enabler&, const T& val) : value(val) + { + // nothing; note that users cannot actually invoke this function + // because they lack access to the make_shared_enabler. + } + + bool is_value() const override + { + return true; + } + + /** + * Gets the data associated with this value. + */ + T& get() + { + return data_; + } + + /** + * Gets the data associated with this value. Const version. + */ + const T& get() const + { + return data_; + } + + private: + T data_; + + /** + * Constructs a value from the given data. + */ + value(const T& val) : data_(val) + { + } + + value(const value& val) = delete; + value& operator=(const value& val) = delete; +}; + +template <class T> +std::shared_ptr<typename value_traits<T>::type> make_value(T&& val) +{ + using value_type = typename value_traits<T>::type; + using enabler = typename value_type::make_shared_enabler; + return std::make_shared<value_type>( + enabler{}, value_traits<T>::construct(std::forward<T>(val))); +} + +template <class T> +inline std::shared_ptr<value<T>> base::as() +{ + return std::dynamic_pointer_cast<value<T>>(shared_from_this()); +} + +// special case value<double> to allow getting an integer parameter as a +// double value +template <> +inline std::shared_ptr<value<double>> base::as() +{ + if (auto v = std::dynamic_pointer_cast<value<double>>(shared_from_this())) + return v; + + if (auto v = std::dynamic_pointer_cast<value<int64_t>>(shared_from_this())) + return make_value<double>(static_cast<double>(v->get())); + + return nullptr; +} + +template <class T> +inline std::shared_ptr<const value<T>> base::as() const +{ + return std::dynamic_pointer_cast<const value<T>>(shared_from_this()); +} + +// special case value<double> to allow getting an integer parameter as a +// double value +template <> +inline std::shared_ptr<const value<double>> base::as() const +{ + if (auto v + = std::dynamic_pointer_cast<const value<double>>(shared_from_this())) + return v; + + if (auto v = as<int64_t>()) + { + // the below has to be a non-const value<double> due to a bug in + // libc++: https://llvm.org/bugs/show_bug.cgi?id=18843 + return make_value<double>(static_cast<double>(v->get())); + } + + return nullptr; +} + +/** + * Exception class for array insertion errors. + */ +class array_exception : public std::runtime_error +{ + public: + array_exception(const std::string& err) : std::runtime_error{err} + { + } +}; + +class array : public base +{ + public: + friend std::shared_ptr<array> make_array(); + + std::shared_ptr<base> clone() const override; + + virtual bool is_array() const override + { + return true; + } + + using size_type = std::size_t; + + /** + * arrays can be iterated over + */ + using iterator = std::vector<std::shared_ptr<base>>::iterator; + + /** + * arrays can be iterated over. Const version. + */ + using const_iterator = std::vector<std::shared_ptr<base>>::const_iterator; + + iterator begin() + { + return values_.begin(); + } + + const_iterator begin() const + { + return values_.begin(); + } + + iterator end() + { + return values_.end(); + } + + const_iterator end() const + { + return values_.end(); + } + + /** + * Obtains the array (vector) of base values. + */ + std::vector<std::shared_ptr<base>>& get() + { + return values_; + } + + /** + * Obtains the array (vector) of base values. Const version. + */ + const std::vector<std::shared_ptr<base>>& get() const + { + return values_; + } + + std::shared_ptr<base> at(size_t idx) const + { + return values_.at(idx); + } + + /** + * Obtains an array of value<T>s. Note that elements may be + * nullptr if they cannot be converted to a value<T>. + */ + template <class T> + std::vector<std::shared_ptr<value<T>>> array_of() const + { + std::vector<std::shared_ptr<value<T>>> result(values_.size()); + + std::transform(values_.begin(), values_.end(), result.begin(), + [&](std::shared_ptr<base> v) { return v->as<T>(); }); + + return result; + } + + /** + * Obtains a option<vector<T>>. The option will be empty if the array + * contains values that are not of type T. + */ + template <class T> + inline typename array_of_trait<T>::return_type get_array_of() const + { + std::vector<T> result; + result.reserve(values_.size()); + + for (const auto& val : values_) + { + if (auto v = val->as<T>()) + result.push_back(v->get()); + else + return {}; + } + + return {std::move(result)}; + } + + /** + * Obtains an array of arrays. Note that elements may be nullptr + * if they cannot be converted to a array. + */ + std::vector<std::shared_ptr<array>> nested_array() const + { + std::vector<std::shared_ptr<array>> result(values_.size()); + + std::transform(values_.begin(), values_.end(), result.begin(), + [&](std::shared_ptr<base> v) -> std::shared_ptr<array> { + if (v->is_array()) + return std::static_pointer_cast<array>(v); + return std::shared_ptr<array>{}; + }); + + return result; + } + + /** + * Add a value to the end of the array + */ + template <class T> + void push_back(const std::shared_ptr<value<T>>& val) + { + if (values_.empty() || values_[0]->as<T>()) + { + values_.push_back(val); + } + else + { + throw array_exception{"Arrays must be homogenous."}; + } + } + + /** + * Add an array to the end of the array + */ + void push_back(const std::shared_ptr<array>& val) + { + if (values_.empty() || values_[0]->is_array()) + { + values_.push_back(val); + } + else + { + throw array_exception{"Arrays must be homogenous."}; + } + } + + /** + * Convenience function for adding a simple element to the end + * of the array. + */ + template <class T> + void push_back(T&& val, typename value_traits<T>::type* = 0) + { + push_back(make_value(std::forward<T>(val))); + } + + /** + * Insert a value into the array + */ + template <class T> + iterator insert(iterator position, const std::shared_ptr<value<T>>& value) + { + if (values_.empty() || values_[0]->as<T>()) + { + return values_.insert(position, value); + } + else + { + throw array_exception{"Arrays must be homogenous."}; + } + } + + /** + * Insert an array into the array + */ + iterator insert(iterator position, const std::shared_ptr<array>& value) + { + if (values_.empty() || values_[0]->is_array()) + { + return values_.insert(position, value); + } + else + { + throw array_exception{"Arrays must be homogenous."}; + } + } + + /** + * Convenience function for inserting a simple element in the array + */ + template <class T> + iterator insert(iterator position, T&& val, + typename value_traits<T>::type* = 0) + { + return insert(position, make_value(std::forward<T>(val))); + } + + /** + * Erase an element from the array + */ + iterator erase(iterator position) + { + return values_.erase(position); + } + + /** + * Clear the array + */ + void clear() + { + values_.clear(); + } + + /** + * Reserve space for n values. + */ + void reserve(size_type n) + { + values_.reserve(n); + } + + private: + array() = default; + + template <class InputIterator> + array(InputIterator begin, InputIterator end) : values_{begin, end} + { + // nothing + } + + array(const array& obj) = delete; + array& operator=(const array& obj) = delete; + + std::vector<std::shared_ptr<base>> values_; +}; + +inline std::shared_ptr<array> make_array() +{ + struct make_shared_enabler : public array + { + make_shared_enabler() + { + // nothing + } + }; + + return std::make_shared<make_shared_enabler>(); +} + +template <> +inline std::shared_ptr<array> make_element<array>() +{ + return make_array(); +} + +/** + * Obtains a option<vector<T>>. The option will be empty if the array + * contains values that are not of type T. + */ +template <> +inline typename array_of_trait<array>::return_type +array::get_array_of<array>() const +{ + std::vector<std::shared_ptr<array>> result; + result.reserve(values_.size()); + + for (const auto& val : values_) + { + if (auto v = val->as_array()) + result.push_back(v); + else + return {}; + } + + return {std::move(result)}; +} + +class table; + +class table_array : public base +{ + friend class table; + friend std::shared_ptr<table_array> make_table_array(); + + public: + std::shared_ptr<base> clone() const override; + + using size_type = std::size_t; + + /** + * arrays can be iterated over + */ + using iterator = std::vector<std::shared_ptr<table>>::iterator; + + /** + * arrays can be iterated over. Const version. + */ + using const_iterator = std::vector<std::shared_ptr<table>>::const_iterator; + + iterator begin() + { + return array_.begin(); + } + + const_iterator begin() const + { + return array_.begin(); + } + + iterator end() + { + return array_.end(); + } + + const_iterator end() const + { + return array_.end(); + } + + virtual bool is_table_array() const override + { + return true; + } + + std::vector<std::shared_ptr<table>>& get() + { + return array_; + } + + const std::vector<std::shared_ptr<table>>& get() const + { + return array_; + } + + /** + * Add a table to the end of the array + */ + void push_back(const std::shared_ptr<table>& val) + { + array_.push_back(val); + } + + /** + * Insert a table into the array + */ + iterator insert(iterator position, const std::shared_ptr<table>& value) + { + return array_.insert(position, value); + } + + /** + * Erase an element from the array + */ + iterator erase(iterator position) + { + return array_.erase(position); + } + + /** + * Clear the array + */ + void clear() + { + array_.clear(); + } + + /** + * Reserve space for n tables. + */ + void reserve(size_type n) + { + array_.reserve(n); + } + + private: + table_array() + { + // nothing + } + + table_array(const table_array& obj) = delete; + table_array& operator=(const table_array& rhs) = delete; + + std::vector<std::shared_ptr<table>> array_; +}; + +inline std::shared_ptr<table_array> make_table_array() +{ + struct make_shared_enabler : public table_array + { + make_shared_enabler() + { + // nothing + } + }; + + return std::make_shared<make_shared_enabler>(); +} + +template <> +inline std::shared_ptr<table_array> make_element<table_array>() +{ + return make_table_array(); +} + +// The below are overloads for fetching specific value types out of a value +// where special casting behavior (like bounds checking) is desired + +template <class T> +typename std::enable_if<!std::is_floating_point<T>::value + && std::is_signed<T>::value, + option<T>>::type +get_impl(const std::shared_ptr<base>& elem) +{ + if (auto v = elem->as<int64_t>()) + { + if (v->get() < std::numeric_limits<T>::min()) + throw std::underflow_error{ + "T cannot represent the value requested in get"}; + + if (v->get() > std::numeric_limits<T>::max()) + throw std::overflow_error{ + "T cannot represent the value requested in get"}; + + return {static_cast<T>(v->get())}; + } + else + { + return {}; + } +} + +template <class T> +typename std::enable_if<!std::is_same<T, bool>::value + && std::is_unsigned<T>::value, + option<T>>::type +get_impl(const std::shared_ptr<base>& elem) +{ + if (auto v = elem->as<int64_t>()) + { + if (v->get() < 0) + throw std::underflow_error{"T cannot store negative value in get"}; + + if (static_cast<uint64_t>(v->get()) > std::numeric_limits<T>::max()) + throw std::overflow_error{ + "T cannot represent the value requested in get"}; + + return {static_cast<T>(v->get())}; + } + else + { + return {}; + } +} + +template <class T> +typename std::enable_if<!std::is_integral<T>::value + || std::is_same<T, bool>::value, + option<T>>::type +get_impl(const std::shared_ptr<base>& elem) +{ + if (auto v = elem->as<T>()) + { + return {v->get()}; + } + else + { + return {}; + } +} + +/** + * Represents a TOML keytable. + */ +class table : public base +{ + public: + friend class table_array; + friend std::shared_ptr<table> make_table(); + + std::shared_ptr<base> clone() const override; + + /** + * tables can be iterated over. + */ + using iterator = string_to_base_map::iterator; + + /** + * tables can be iterated over. Const version. + */ + using const_iterator = string_to_base_map::const_iterator; + + iterator begin() + { + return map_.begin(); + } + + const_iterator begin() const + { + return map_.begin(); + } + + iterator end() + { + return map_.end(); + } + + const_iterator end() const + { + return map_.end(); + } + + bool is_table() const override + { + return true; + } + + bool empty() const + { + return map_.empty(); + } + + /** + * Determines if this key table contains the given key. + */ + bool contains(const std::string& key) const + { + return map_.find(key) != map_.end(); + } + + /** + * Determines if this key table contains the given key. Will + * resolve "qualified keys". Qualified keys are the full access + * path separated with dots like "grandparent.parent.child". + */ + bool contains_qualified(const std::string& key) const + { + return resolve_qualified(key); + } + + /** + * Obtains the base for a given key. + * @throw std::out_of_range if the key does not exist + */ + std::shared_ptr<base> get(const std::string& key) const + { + return map_.at(key); + } + + /** + * Obtains the base for a given key. Will resolve "qualified + * keys". Qualified keys are the full access path separated with + * dots like "grandparent.parent.child". + * + * @throw std::out_of_range if the key does not exist + */ + std::shared_ptr<base> get_qualified(const std::string& key) const + { + std::shared_ptr<base> p; + resolve_qualified(key, &p); + return p; + } + + /** + * Obtains a table for a given key, if possible. + */ + std::shared_ptr<table> get_table(const std::string& key) const + { + if (contains(key) && get(key)->is_table()) + return std::static_pointer_cast<table>(get(key)); + return nullptr; + } + + /** + * Obtains a table for a given key, if possible. Will resolve + * "qualified keys". + */ + std::shared_ptr<table> get_table_qualified(const std::string& key) const + { + if (contains_qualified(key) && get_qualified(key)->is_table()) + return std::static_pointer_cast<table>(get_qualified(key)); + return nullptr; + } + + /** + * Obtains an array for a given key. + */ + std::shared_ptr<array> get_array(const std::string& key) const + { + if (!contains(key)) + return nullptr; + return get(key)->as_array(); + } + + /** + * Obtains an array for a given key. Will resolve "qualified keys". + */ + std::shared_ptr<array> get_array_qualified(const std::string& key) const + { + if (!contains_qualified(key)) + return nullptr; + return get_qualified(key)->as_array(); + } + + /** + * Obtains a table_array for a given key, if possible. + */ + std::shared_ptr<table_array> get_table_array(const std::string& key) const + { + if (!contains(key)) + return nullptr; + return get(key)->as_table_array(); + } + + /** + * Obtains a table_array for a given key, if possible. Will resolve + * "qualified keys". + */ + std::shared_ptr<table_array> + get_table_array_qualified(const std::string& key) const + { + if (!contains_qualified(key)) + return nullptr; + return get_qualified(key)->as_table_array(); + } + + /** + * Helper function that attempts to get a value corresponding + * to the template parameter from a given key. + */ + template <class T> + option<T> get_as(const std::string& key) const + { + try + { + return get_impl<T>(get(key)); + } + catch (const std::out_of_range&) + { + return {}; + } + } + + /** + * Helper function that attempts to get a value corresponding + * to the template parameter from a given key. Will resolve "qualified + * keys". + */ + template <class T> + option<T> get_qualified_as(const std::string& key) const + { + try + { + return get_impl<T>(get_qualified(key)); + } + catch (const std::out_of_range&) + { + return {}; + } + } + + /** + * Helper function that attempts to get an array of values of a given + * type corresponding to the template parameter for a given key. + * + * If the key doesn't exist, doesn't exist as an array type, or one or + * more keys inside the array type are not of type T, an empty option + * is returned. Otherwise, an option containing a vector of the values + * is returned. + */ + template <class T> + inline typename array_of_trait<T>::return_type + get_array_of(const std::string& key) const + { + if (auto v = get_array(key)) + { + std::vector<T> result; + result.reserve(v->get().size()); + + for (const auto& b : v->get()) + { + if (auto val = b->as<T>()) + result.push_back(val->get()); + else + return {}; + } + return {std::move(result)}; + } + + return {}; + } + + /** + * Helper function that attempts to get an array of values of a given + * type corresponding to the template parameter for a given key. Will + * resolve "qualified keys". + * + * If the key doesn't exist, doesn't exist as an array type, or one or + * more keys inside the array type are not of type T, an empty option + * is returned. Otherwise, an option containing a vector of the values + * is returned. + */ + template <class T> + inline typename array_of_trait<T>::return_type + get_qualified_array_of(const std::string& key) const + { + if (auto v = get_array_qualified(key)) + { + std::vector<T> result; + result.reserve(v->get().size()); + + for (const auto& b : v->get()) + { + if (auto val = b->as<T>()) + result.push_back(val->get()); + else + return {}; + } + return {std::move(result)}; + } + + return {}; + } + + /** + * Adds an element to the keytable. + */ + void insert(const std::string& key, const std::shared_ptr<base>& value) + { + map_[key] = value; + } + + /** + * Convenience shorthand for adding a simple element to the + * keytable. + */ + template <class T> + void insert(const std::string& key, T&& val, + typename value_traits<T>::type* = 0) + { + insert(key, make_value(std::forward<T>(val))); + } + + /** + * Removes an element from the table. + */ + void erase(const std::string& key) + { + map_.erase(key); + } + + private: + table() + { + // nothing + } + + table(const table& obj) = delete; + table& operator=(const table& rhs) = delete; + + std::vector<std::string> split(const std::string& value, + char separator) const + { + std::vector<std::string> result; + std::string::size_type p = 0; + std::string::size_type q; + while ((q = value.find(separator, p)) != std::string::npos) + { + result.emplace_back(value, p, q - p); + p = q + 1; + } + result.emplace_back(value, p); + return result; + } + + // If output parameter p is specified, fill it with the pointer to the + // specified entry and throw std::out_of_range if it couldn't be found. + // + // Otherwise, just return true if the entry could be found or false + // otherwise and do not throw. + bool resolve_qualified(const std::string& key, + std::shared_ptr<base>* p = nullptr) const + { + auto parts = split(key, '.'); + auto last_key = parts.back(); + parts.pop_back(); + + auto table = this; + for (const auto& part : parts) + { + table = table->get_table(part).get(); + if (!table) + { + if (!p) + return false; + + throw std::out_of_range{key + " is not a valid key"}; + } + } + + if (!p) + return table->map_.count(last_key) != 0; + + *p = table->map_.at(last_key); + return true; + } + + string_to_base_map map_; +}; + +/** + * Helper function that attempts to get an array of arrays for a given + * key. + * + * If the key doesn't exist, doesn't exist as an array type, or one or + * more keys inside the array type are not of type T, an empty option + * is returned. Otherwise, an option containing a vector of the values + * is returned. + */ +template <> +inline typename array_of_trait<array>::return_type +table::get_array_of<array>(const std::string& key) const +{ + if (auto v = get_array(key)) + { + std::vector<std::shared_ptr<array>> result; + result.reserve(v->get().size()); + + for (const auto& b : v->get()) + { + if (auto val = b->as_array()) + result.push_back(val); + else + return {}; + } + + return {std::move(result)}; + } + + return {}; +} + +/** + * Helper function that attempts to get an array of arrays for a given + * key. Will resolve "qualified keys". + * + * If the key doesn't exist, doesn't exist as an array type, or one or + * more keys inside the array type are not of type T, an empty option + * is returned. Otherwise, an option containing a vector of the values + * is returned. + */ +template <> +inline typename array_of_trait<array>::return_type +table::get_qualified_array_of<array>(const std::string& key) const +{ + if (auto v = get_array_qualified(key)) + { + std::vector<std::shared_ptr<array>> result; + result.reserve(v->get().size()); + + for (const auto& b : v->get()) + { + if (auto val = b->as_array()) + result.push_back(val); + else + return {}; + } + + return {std::move(result)}; + } + + return {}; +} + +std::shared_ptr<table> make_table() +{ + struct make_shared_enabler : public table + { + make_shared_enabler() + { + // nothing + } + }; + + return std::make_shared<make_shared_enabler>(); +} + +template <> +inline std::shared_ptr<table> make_element<table>() +{ + return make_table(); +} + +template <class T> +std::shared_ptr<base> value<T>::clone() const +{ + return make_value(data_); +} + +inline std::shared_ptr<base> array::clone() const +{ + auto result = make_array(); + result->reserve(values_.size()); + for (const auto& ptr : values_) + result->values_.push_back(ptr->clone()); + return result; +} + +inline std::shared_ptr<base> table_array::clone() const +{ + auto result = make_table_array(); + result->reserve(array_.size()); + for (const auto& ptr : array_) + result->array_.push_back(ptr->clone()->as_table()); + return result; +} + +inline std::shared_ptr<base> table::clone() const +{ + auto result = make_table(); + for (const auto& pr : map_) + result->insert(pr.first, pr.second->clone()); + return result; +} + +/** + * Exception class for all TOML parsing errors. + */ +class parse_exception : public std::runtime_error +{ + public: + parse_exception(const std::string& err) : std::runtime_error{err} + { + } + + parse_exception(const std::string& err, std::size_t line_number) + : std::runtime_error{err + " at line " + std::to_string(line_number)} + { + } +}; + +inline bool is_number(char c) +{ + return c >= '0' && c <= '9'; +} + +/** + * Helper object for consuming expected characters. + */ +template <class OnError> +class consumer +{ + public: + consumer(std::string::iterator& it, const std::string::iterator& end, + OnError&& on_error) + : it_(it), end_(end), on_error_(std::forward<OnError>(on_error)) + { + // nothing + } + + void operator()(char c) + { + if (it_ == end_ || *it_ != c) + on_error_(); + ++it_; + } + + template <std::size_t N> + void operator()(const char (&str)[N]) + { + std::for_each(std::begin(str), std::end(str) - 1, + [&](char c) { (*this)(c); }); + } + + int eat_digits(int len) + { + int val = 0; + for (int i = 0; i < len; ++i) + { + if (!is_number(*it_) || it_ == end_) + on_error_(); + val = 10 * val + (*it_++ - '0'); + } + return val; + } + + void error() + { + on_error_(); + } + + private: + std::string::iterator& it_; + const std::string::iterator& end_; + OnError on_error_; +}; + +template <class OnError> +consumer<OnError> make_consumer(std::string::iterator& it, + const std::string::iterator& end, + OnError&& on_error) +{ + return consumer<OnError>(it, end, std::forward<OnError>(on_error)); +} + +// replacement for std::getline to handle incorrectly line-ended files +// https://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf +namespace detail +{ +inline std::istream& getline(std::istream& input, std::string& line) +{ + line.clear(); + + std::istream::sentry sentry{input, true}; + auto sb = input.rdbuf(); + + while (true) + { + auto c = sb->sbumpc(); + if (c == '\r') + { + if (sb->sgetc() == '\n') + c = sb->sbumpc(); + } + + if (c == '\n') + return input; + + if (c == std::istream::traits_type::eof()) + { + if (line.empty()) + input.setstate(std::ios::eofbit); + return input; + } + + line.push_back(static_cast<char>(c)); + } +} +} + +/** + * The parser class. + */ +class parser +{ + public: + /** + * Parsers are constructed from streams. + */ + parser(std::istream& stream) : input_(stream) + { + // nothing + } + + parser& operator=(const parser& parser) = delete; + + /** + * Parses the stream this parser was created on until EOF. + * @throw parse_exception if there are errors in parsing + */ + std::shared_ptr<table> parse() + { + std::shared_ptr<table> root = make_table(); + + table* curr_table = root.get(); + + while (detail::getline(input_, line_)) + { + line_number_++; + auto it = line_.begin(); + auto end = line_.end(); + consume_whitespace(it, end); + if (it == end || *it == '#') + continue; + if (*it == '[') + { + curr_table = root.get(); + parse_table(it, end, curr_table); + } + else + { + parse_key_value(it, end, curr_table); + consume_whitespace(it, end); + eol_or_comment(it, end); + } + } + return root; + } + + private: +#if defined _MSC_VER + __declspec(noreturn) +#elif defined __GNUC__ + __attribute__((noreturn)) +#endif + void throw_parse_exception(const std::string& err) + { + throw parse_exception{err, line_number_}; + } + + void parse_table(std::string::iterator& it, + const std::string::iterator& end, table*& curr_table) + { + // remove the beginning keytable marker + ++it; + if (it == end) + throw_parse_exception("Unexpected end of table"); + if (*it == '[') + parse_table_array(it, end, curr_table); + else + parse_single_table(it, end, curr_table); + } + + void parse_single_table(std::string::iterator& it, + const std::string::iterator& end, + table*& curr_table) + { + if (it == end || *it == ']') + throw_parse_exception("Table name cannot be empty"); + + std::string full_table_name; + bool inserted = false; + while (it != end && *it != ']') + { + auto part = parse_key(it, end, + [](char c) { return c == '.' || c == ']'; }); + + if (part.empty()) + throw_parse_exception("Empty component of table name"); + + if (!full_table_name.empty()) + full_table_name += "."; + full_table_name += part; + + if (curr_table->contains(part)) + { + auto b = curr_table->get(part); + if (b->is_table()) + curr_table = static_cast<table*>(b.get()); + else if (b->is_table_array()) + curr_table = std::static_pointer_cast<table_array>(b) + ->get() + .back() + .get(); + else + throw_parse_exception("Key " + full_table_name + + "already exists as a value"); + } + else + { + inserted = true; + curr_table->insert(part, make_table()); + curr_table = static_cast<table*>(curr_table->get(part).get()); + } + consume_whitespace(it, end); + if (it != end && *it == '.') + ++it; + consume_whitespace(it, end); + } + + if (it == end) + throw_parse_exception( + "Unterminated table declaration; did you forget a ']'?"); + + // table already existed + if (!inserted) + { + auto is_value + = [](const std::pair<const std::string&, + const std::shared_ptr<base>&>& p) { + return p.second->is_value(); + }; + + // if there are any values, we can't add values to this table + // since it has already been defined. If there aren't any + // values, then it was implicitly created by something like + // [a.b] + if (curr_table->empty() || std::any_of(curr_table->begin(), + curr_table->end(), is_value)) + { + throw_parse_exception("Redefinition of table " + + full_table_name); + } + } + + ++it; + consume_whitespace(it, end); + eol_or_comment(it, end); + } + + void parse_table_array(std::string::iterator& it, + const std::string::iterator& end, table*& curr_table) + { + ++it; + if (it == end || *it == ']') + throw_parse_exception("Table array name cannot be empty"); + + std::string full_ta_name; + while (it != end && *it != ']') + { + auto part = parse_key(it, end, + [](char c) { return c == '.' || c == ']'; }); + + if (part.empty()) + throw_parse_exception("Empty component of table array name"); + + if (!full_ta_name.empty()) + full_ta_name += "."; + full_ta_name += part; + + consume_whitespace(it, end); + if (it != end && *it == '.') + ++it; + consume_whitespace(it, end); + + if (curr_table->contains(part)) + { + auto b = curr_table->get(part); + + // if this is the end of the table array name, add an + // element to the table array that we just looked up + if (it != end && *it == ']') + { + if (!b->is_table_array()) + throw_parse_exception("Key " + full_ta_name + + " is not a table array"); + auto v = b->as_table_array(); + v->get().push_back(make_table()); + curr_table = v->get().back().get(); + } + // otherwise, just keep traversing down the key name + else + { + if (b->is_table()) + curr_table = static_cast<table*>(b.get()); + else if (b->is_table_array()) + curr_table = std::static_pointer_cast<table_array>(b) + ->get() + .back() + .get(); + else + throw_parse_exception("Key " + full_ta_name + + " already exists as a value"); + } + } + else + { + // if this is the end of the table array name, add a new + // table array and a new table inside that array for us to + // add keys to next + if (it != end && *it == ']') + { + curr_table->insert(part, make_table_array()); + auto arr = std::static_pointer_cast<table_array>( + curr_table->get(part)); + arr->get().push_back(make_table()); + curr_table = arr->get().back().get(); + } + // otherwise, create the implicitly defined table and move + // down to it + else + { + curr_table->insert(part, make_table()); + curr_table + = static_cast<table*>(curr_table->get(part).get()); + } + } + } + + // consume the last "]]" + if (it == end) + throw_parse_exception("Unterminated table array name"); + ++it; + if (it == end) + throw_parse_exception("Unterminated table array name"); + ++it; + + consume_whitespace(it, end); + eol_or_comment(it, end); + } + + void parse_key_value(std::string::iterator& it, std::string::iterator& end, + table* curr_table) + { + auto key = parse_key(it, end, [](char c) { return c == '='; }); + if (curr_table->contains(key)) + throw_parse_exception("Key " + key + " already present"); + if (it == end || *it != '=') + throw_parse_exception("Value must follow after a '='"); + ++it; + consume_whitespace(it, end); + curr_table->insert(key, parse_value(it, end)); + consume_whitespace(it, end); + } + + template <class Function> + std::string parse_key(std::string::iterator& it, + const std::string::iterator& end, Function&& fun) + { + consume_whitespace(it, end); + if (*it == '"') + { + return parse_quoted_key(it, end); + } + else + { + auto bke = std::find_if(it, end, std::forward<Function>(fun)); + return parse_bare_key(it, bke); + } + } + + std::string parse_bare_key(std::string::iterator& it, + const std::string::iterator& end) + { + if (it == end) + { + throw_parse_exception("Bare key missing name"); + } + + auto key_end = end; + --key_end; + consume_backwards_whitespace(key_end, it); + ++key_end; + std::string key{it, key_end}; + + if (std::find(it, key_end, '#') != key_end) + { + throw_parse_exception("Bare key " + key + " cannot contain #"); + } + + if (std::find_if(it, key_end, + [](char c) { return c == ' ' || c == '\t'; }) + != key_end) + { + throw_parse_exception("Bare key " + key + + " cannot contain whitespace"); + } + + if (std::find_if(it, key_end, + [](char c) { return c == '[' || c == ']'; }) + != key_end) + { + throw_parse_exception("Bare key " + key + + " cannot contain '[' or ']'"); + } + + it = end; + return key; + } + + std::string parse_quoted_key(std::string::iterator& it, + const std::string::iterator& end) + { + return string_literal(it, end, '"'); + } + + enum class parse_type + { + STRING = 1, + LOCAL_TIME, + LOCAL_DATE, + LOCAL_DATETIME, + OFFSET_DATETIME, + INT, + FLOAT, + BOOL, + ARRAY, + INLINE_TABLE + }; + + std::shared_ptr<base> parse_value(std::string::iterator& it, + std::string::iterator& end) + { + parse_type type = determine_value_type(it, end); + switch (type) + { + case parse_type::STRING: + return parse_string(it, end); + case parse_type::LOCAL_TIME: + return parse_time(it, end); + case parse_type::LOCAL_DATE: + case parse_type::LOCAL_DATETIME: + case parse_type::OFFSET_DATETIME: + return parse_date(it, end); + case parse_type::INT: + case parse_type::FLOAT: + return parse_number(it, end); + case parse_type::BOOL: + return parse_bool(it, end); + case parse_type::ARRAY: + return parse_array(it, end); + case parse_type::INLINE_TABLE: + return parse_inline_table(it, end); + default: + throw_parse_exception("Failed to parse value"); + } + } + + parse_type determine_value_type(const std::string::iterator& it, + const std::string::iterator& end) + { + if (*it == '"' || *it == '\'') + { + return parse_type::STRING; + } + else if (is_time(it, end)) + { + return parse_type::LOCAL_TIME; + } + else if (auto dtype = date_type(it, end)) + { + return *dtype; + } + else if (is_number(*it) || *it == '-' || *it == '+') + { + return determine_number_type(it, end); + } + else if (*it == 't' || *it == 'f') + { + return parse_type::BOOL; + } + else if (*it == '[') + { + return parse_type::ARRAY; + } + else if (*it == '{') + { + return parse_type::INLINE_TABLE; + } + throw_parse_exception("Failed to parse value type"); + } + + parse_type determine_number_type(const std::string::iterator& it, + const std::string::iterator& end) + { + // determine if we are an integer or a float + auto check_it = it; + if (*check_it == '-' || *check_it == '+') + ++check_it; + while (check_it != end && is_number(*check_it)) + ++check_it; + if (check_it != end && *check_it == '.') + { + ++check_it; + while (check_it != end && is_number(*check_it)) + ++check_it; + return parse_type::FLOAT; + } + else + { + return parse_type::INT; + } + } + + std::shared_ptr<value<std::string>> parse_string(std::string::iterator& it, + std::string::iterator& end) + { + auto delim = *it; + assert(delim == '"' || delim == '\''); + + // end is non-const here because we have to be able to potentially + // parse multiple lines in a string, not just one + auto check_it = it; + ++check_it; + if (check_it != end && *check_it == delim) + { + ++check_it; + if (check_it != end && *check_it == delim) + { + it = ++check_it; + return parse_multiline_string(it, end, delim); + } + } + return make_value<std::string>(string_literal(it, end, delim)); + } + + std::shared_ptr<value<std::string>> + parse_multiline_string(std::string::iterator& it, + std::string::iterator& end, char delim) + { + std::stringstream ss; + + auto is_ws = [](char c) { return c == ' ' || c == '\t'; }; + + bool consuming = false; + std::shared_ptr<value<std::string>> ret; + + auto handle_line + = [&](std::string::iterator& it, std::string::iterator& end) { + if (consuming) + { + it = std::find_if_not(it, end, is_ws); + + // whole line is whitespace + if (it == end) + return; + } + + consuming = false; + + while (it != end) + { + // handle escaped characters + if (delim == '"' && *it == '\\') + { + auto check = it; + // check if this is an actual escape sequence or a + // whitespace escaping backslash + ++check; + consume_whitespace(check, end); + if (check == end) + { + consuming = true; + break; + } + + ss << parse_escape_code(it, end); + continue; + } + + // if we can end the string + if (std::distance(it, end) >= 3) + { + auto check = it; + // check for """ + if (*check++ == delim && *check++ == delim + && *check++ == delim) + { + it = check; + ret = make_value<std::string>(ss.str()); + break; + } + } + + ss << *it++; + } + }; + + // handle the remainder of the current line + handle_line(it, end); + if (ret) + return ret; + + // start eating lines + while (detail::getline(input_, line_)) + { + ++line_number_; + + it = line_.begin(); + end = line_.end(); + + handle_line(it, end); + + if (ret) + return ret; + + if (!consuming) + ss << std::endl; + } + + throw_parse_exception("Unterminated multi-line basic string"); + } + + std::string string_literal(std::string::iterator& it, + const std::string::iterator& end, char delim) + { + ++it; + std::string val; + while (it != end) + { + // handle escaped characters + if (delim == '"' && *it == '\\') + { + val += parse_escape_code(it, end); + } + else if (*it == delim) + { + ++it; + consume_whitespace(it, end); + return val; + } + else + { + val += *it++; + } + } + throw_parse_exception("Unterminated string literal"); + } + + std::string parse_escape_code(std::string::iterator& it, + const std::string::iterator& end) + { + ++it; + if (it == end) + throw_parse_exception("Invalid escape sequence"); + char value; + if (*it == 'b') + { + value = '\b'; + } + else if (*it == 't') + { + value = '\t'; + } + else if (*it == 'n') + { + value = '\n'; + } + else if (*it == 'f') + { + value = '\f'; + } + else if (*it == 'r') + { + value = '\r'; + } + else if (*it == '"') + { + value = '"'; + } + else if (*it == '\\') + { + value = '\\'; + } + else if (*it == 'u' || *it == 'U') + { + return parse_unicode(it, end); + } + else + { + throw_parse_exception("Invalid escape sequence"); + } + ++it; + return std::string(1, value); + } + + std::string parse_unicode(std::string::iterator& it, + const std::string::iterator& end) + { + bool large = *it++ == 'U'; + auto codepoint = parse_hex(it, end, large ? 0x10000000 : 0x1000); + + if ((codepoint > 0xd7ff && codepoint < 0xe000) || codepoint > 0x10ffff) + { + throw_parse_exception( + "Unicode escape sequence is not a Unicode scalar value"); + } + + std::string result; + // See Table 3-6 of the Unicode standard + if (codepoint <= 0x7f) + { + // 1-byte codepoints: 00000000 0xxxxxxx + // repr: 0xxxxxxx + result += static_cast<char>(codepoint & 0x7f); + } + else if (codepoint <= 0x7ff) + { + // 2-byte codepoints: 00000yyy yyxxxxxx + // repr: 110yyyyy 10xxxxxx + // + // 0x1f = 00011111 + // 0xc0 = 11000000 + // + result += static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f)); + // + // 0x80 = 10000000 + // 0x3f = 00111111 + // + result += static_cast<char>(0x80 | (codepoint & 0x3f)); + } + else if (codepoint <= 0xffff) + { + // 3-byte codepoints: zzzzyyyy yyxxxxxx + // repr: 1110zzzz 10yyyyyy 10xxxxxx + // + // 0xe0 = 11100000 + // 0x0f = 00001111 + // + result += static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f)); + result += static_cast<char>(0x80 | ((codepoint >> 6) & 0x1f)); + result += static_cast<char>(0x80 | (codepoint & 0x3f)); + } + else + { + // 4-byte codepoints: 000uuuuu zzzzyyyy yyxxxxxx + // repr: 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx + // + // 0xf0 = 11110000 + // 0x07 = 00000111 + // + result += static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07)); + result += static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f)); + result += static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)); + result += static_cast<char>(0x80 | (codepoint & 0x3f)); + } + return result; + } + + uint32_t parse_hex(std::string::iterator& it, + const std::string::iterator& end, uint32_t place) + { + uint32_t value = 0; + while (place > 0) + { + if (it == end) + throw_parse_exception("Unexpected end of unicode sequence"); + + if (!is_hex(*it)) + throw_parse_exception("Invalid unicode escape sequence"); + + value += place * hex_to_digit(*it++); + place /= 16; + } + return value; + } + + bool is_hex(char c) + { + return is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); + } + + uint32_t hex_to_digit(char c) + { + if (is_number(c)) + return static_cast<uint32_t>(c - '0'); + return 10 + static_cast<uint32_t>( + c - ((c >= 'a' && c <= 'f') ? 'a' : 'A')); + } + + std::shared_ptr<base> parse_number(std::string::iterator& it, + const std::string::iterator& end) + { + auto check_it = it; + auto check_end = find_end_of_number(it, end); + + auto eat_sign = [&]() { + if (check_it != end && (*check_it == '-' || *check_it == '+')) + ++check_it; + }; + + eat_sign(); + + auto eat_numbers = [&]() { + auto beg = check_it; + while (check_it != end && is_number(*check_it)) + { + ++check_it; + if (check_it != end && *check_it == '_') + { + ++check_it; + if (check_it == end || !is_number(*check_it)) + throw_parse_exception("Malformed number"); + } + } + + if (check_it == beg) + throw_parse_exception("Malformed number"); + }; + + auto check_no_leading_zero = [&]() { + if (check_it != end && *check_it == '0' && check_it + 1 != check_end + && check_it[1] != '.') + { + throw_parse_exception("Numbers may not have leading zeros"); + } + }; + + check_no_leading_zero(); + eat_numbers(); + + if (check_it != end + && (*check_it == '.' || *check_it == 'e' || *check_it == 'E')) + { + bool is_exp = *check_it == 'e' || *check_it == 'E'; + + ++check_it; + if (check_it == end) + throw_parse_exception("Floats must have trailing digits"); + + auto eat_exp = [&]() { + eat_sign(); + check_no_leading_zero(); + eat_numbers(); + }; + + if (is_exp) + eat_exp(); + else + eat_numbers(); + + if (!is_exp && check_it != end + && (*check_it == 'e' || *check_it == 'E')) + { + ++check_it; + eat_exp(); + } + + return parse_float(it, check_it); + } + else + { + return parse_int(it, check_it); + } + } + + std::shared_ptr<value<int64_t>> parse_int(std::string::iterator& it, + const std::string::iterator& end) + { + std::string v{it, end}; + v.erase(std::remove(v.begin(), v.end(), '_'), v.end()); + it = end; + try + { + return make_value<int64_t>(std::stoll(v)); + } + catch (const std::invalid_argument& ex) + { + throw_parse_exception("Malformed number (invalid argument: " + + std::string{ex.what()} + ")"); + } + catch (const std::out_of_range& ex) + { + throw_parse_exception("Malformed number (out of range: " + + std::string{ex.what()} + ")"); + } + } + + std::shared_ptr<value<double>> parse_float(std::string::iterator& it, + const std::string::iterator& end) + { + std::string v{it, end}; + v.erase(std::remove(v.begin(), v.end(), '_'), v.end()); + it = end; + try + { + return make_value<double>(std::stod(v)); + } + catch (const std::invalid_argument& ex) + { + throw_parse_exception("Malformed number (invalid argument: " + + std::string{ex.what()} + ")"); + } + catch (const std::out_of_range& ex) + { + throw_parse_exception("Malformed number (out of range: " + + std::string{ex.what()} + ")"); + } + } + + std::shared_ptr<value<bool>> parse_bool(std::string::iterator& it, + const std::string::iterator& end) + { + auto eat = make_consumer(it, end, [this]() { + throw_parse_exception("Attempted to parse invalid boolean value"); + }); + + if (*it == 't') + { + eat("true"); + return make_value<bool>(true); + } + else if (*it == 'f') + { + eat("false"); + return make_value<bool>(false); + } + + eat.error(); + return nullptr; + } + + std::string::iterator find_end_of_number(std::string::iterator it, + std::string::iterator end) + { + return std::find_if(it, end, [](char c) { + return !is_number(c) && c != '_' && c != '.' && c != 'e' && c != 'E' + && c != '-' && c != '+'; + }); + } + + std::string::iterator find_end_of_date(std::string::iterator it, + std::string::iterator end) + { + return std::find_if(it, end, [](char c) { + return !is_number(c) && c != 'T' && c != 'Z' && c != ':' && c != '-' + && c != '+' && c != '.'; + }); + } + + std::string::iterator find_end_of_time(std::string::iterator it, + std::string::iterator end) + { + return std::find_if(it, end, [](char c) { + return !is_number(c) && c != ':' && c != '.'; + }); + } + + local_time read_time(std::string::iterator& it, + const std::string::iterator& end) + { + auto time_end = find_end_of_time(it, end); + + auto eat = make_consumer( + it, time_end, [&]() { throw_parse_exception("Malformed time"); }); + + local_time ltime; + + ltime.hour = eat.eat_digits(2); + eat(':'); + ltime.minute = eat.eat_digits(2); + eat(':'); + ltime.second = eat.eat_digits(2); + + int power = 100000; + if (it != time_end && *it == '.') + { + ++it; + while (it != time_end && is_number(*it)) + { + ltime.microsecond += power * (*it++ - '0'); + power /= 10; + } + } + + if (it != time_end) + throw_parse_exception("Malformed time"); + + return ltime; + } + + std::shared_ptr<value<local_time>> + parse_time(std::string::iterator& it, const std::string::iterator& end) + { + return make_value(read_time(it, end)); + } + + std::shared_ptr<base> parse_date(std::string::iterator& it, + const std::string::iterator& end) + { + auto date_end = find_end_of_date(it, end); + + auto eat = make_consumer( + it, date_end, [&]() { throw_parse_exception("Malformed date"); }); + + local_date ldate; + ldate.year = eat.eat_digits(4); + eat('-'); + ldate.month = eat.eat_digits(2); + eat('-'); + ldate.day = eat.eat_digits(2); + + if (it == date_end) + return make_value(ldate); + + eat('T'); + + local_datetime ldt; + static_cast<local_date&>(ldt) = ldate; + static_cast<local_time&>(ldt) = read_time(it, date_end); + + if (it == date_end) + return make_value(ldt); + + offset_datetime dt; + static_cast<local_datetime&>(dt) = ldt; + + int hoff = 0; + int moff = 0; + if (*it == '+' || *it == '-') + { + auto plus = *it == '+'; + ++it; + + hoff = eat.eat_digits(2); + dt.hour_offset = (plus) ? hoff : -hoff; + eat(':'); + moff = eat.eat_digits(2); + dt.minute_offset = (plus) ? moff : -moff; + } + else if (*it == 'Z') + { + ++it; + } + + if (it != date_end) + throw_parse_exception("Malformed date"); + + return make_value(dt); + } + + std::shared_ptr<base> parse_array(std::string::iterator& it, + std::string::iterator& end) + { + // this gets ugly because of the "homogeneity" restriction: + // arrays can either be of only one type, or contain arrays + // (each of those arrays could be of different types, though) + // + // because of the latter portion, we don't really have a choice + // but to represent them as arrays of base values... + ++it; + + // ugh---have to read the first value to determine array type... + skip_whitespace_and_comments(it, end); + + // edge case---empty array + if (*it == ']') + { + ++it; + return make_array(); + } + + auto val_end = std::find_if( + it, end, [](char c) { return c == ',' || c == ']' || c == '#'; }); + parse_type type = determine_value_type(it, val_end); + switch (type) + { + case parse_type::STRING: + return parse_value_array<std::string>(it, end); + case parse_type::LOCAL_TIME: + return parse_value_array<local_time>(it, end); + case parse_type::LOCAL_DATE: + return parse_value_array<local_date>(it, end); + case parse_type::LOCAL_DATETIME: + return parse_value_array<local_datetime>(it, end); + case parse_type::OFFSET_DATETIME: + return parse_value_array<offset_datetime>(it, end); + case parse_type::INT: + return parse_value_array<int64_t>(it, end); + case parse_type::FLOAT: + return parse_value_array<double>(it, end); + case parse_type::BOOL: + return parse_value_array<bool>(it, end); + case parse_type::ARRAY: + return parse_object_array<array>(&parser::parse_array, '[', it, + end); + case parse_type::INLINE_TABLE: + return parse_object_array<table_array>( + &parser::parse_inline_table, '{', it, end); + default: + throw_parse_exception("Unable to parse array"); + } + } + + template <class Value> + std::shared_ptr<array> parse_value_array(std::string::iterator& it, + std::string::iterator& end) + { + auto arr = make_array(); + while (it != end && *it != ']') + { + auto value = parse_value(it, end); + if (auto v = value->as<Value>()) + arr->get().push_back(value); + else + throw_parse_exception("Arrays must be heterogeneous"); + skip_whitespace_and_comments(it, end); + if (*it != ',') + break; + ++it; + skip_whitespace_and_comments(it, end); + } + if (it != end) + ++it; + return arr; + } + + template <class Object, class Function> + std::shared_ptr<Object> parse_object_array(Function&& fun, char delim, + std::string::iterator& it, + std::string::iterator& end) + { + auto arr = make_element<Object>(); + + while (it != end && *it != ']') + { + if (*it != delim) + throw_parse_exception("Unexpected character in array"); + + arr->get().push_back(((*this).*fun)(it, end)); + skip_whitespace_and_comments(it, end); + + if (*it != ',') + break; + + ++it; + skip_whitespace_and_comments(it, end); + } + + if (it == end || *it != ']') + throw_parse_exception("Unterminated array"); + + ++it; + return arr; + } + + std::shared_ptr<table> parse_inline_table(std::string::iterator& it, + std::string::iterator& end) + { + auto tbl = make_table(); + do + { + ++it; + if (it == end) + throw_parse_exception("Unterminated inline table"); + + consume_whitespace(it, end); + parse_key_value(it, end, tbl.get()); + consume_whitespace(it, end); + } while (*it == ','); + + if (it == end || *it != '}') + throw_parse_exception("Unterminated inline table"); + + ++it; + consume_whitespace(it, end); + + return tbl; + } + + void skip_whitespace_and_comments(std::string::iterator& start, + std::string::iterator& end) + { + consume_whitespace(start, end); + while (start == end || *start == '#') + { + if (!detail::getline(input_, line_)) + throw_parse_exception("Unclosed array"); + line_number_++; + start = line_.begin(); + end = line_.end(); + consume_whitespace(start, end); + } + } + + void consume_whitespace(std::string::iterator& it, + const std::string::iterator& end) + { + while (it != end && (*it == ' ' || *it == '\t')) + ++it; + } + + void consume_backwards_whitespace(std::string::iterator& back, + const std::string::iterator& front) + { + while (back != front && (*back == ' ' || *back == '\t')) + --back; + } + + void eol_or_comment(const std::string::iterator& it, + const std::string::iterator& end) + { + if (it != end && *it != '#') + throw_parse_exception("Unidentified trailing character '" + + std::string{*it} + + "'---did you forget a '#'?"); + } + + bool is_time(const std::string::iterator& it, + const std::string::iterator& end) + { + auto time_end = find_end_of_time(it, end); + auto len = std::distance(it, time_end); + + if (len < 8) + return false; + + if (it[2] != ':' || it[5] != ':') + return false; + + if (len > 8) + return it[8] == '.' && len > 9; + + return true; + } + + option<parse_type> date_type(const std::string::iterator& it, + const std::string::iterator& end) + { + auto date_end = find_end_of_date(it, end); + auto len = std::distance(it, date_end); + + if (len < 10) + return {}; + + if (it[4] != '-' || it[7] != '-') + return {}; + + if (len >= 19 && it[10] == 'T' && is_time(it + 11, date_end)) + { + // datetime type + auto time_end = find_end_of_time(it + 11, date_end); + if (time_end == date_end) + return {parse_type::LOCAL_DATETIME}; + else + return {parse_type::OFFSET_DATETIME}; + } + else if (len == 10) + { + // just a regular date + return {parse_type::LOCAL_DATE}; + } + + return {}; + } + + std::istream& input_; + std::string line_; + std::size_t line_number_ = 0; +}; + +/** + * Utility function to parse a file as a TOML file. Returns the root table. + * Throws a parse_exception if the file cannot be opened. + */ +inline std::shared_ptr<table> parse_file(const std::string& filename) +{ +#if defined(BOOST_NOWIDE_FSTREAM_INCLUDED_HPP) + boost::nowide::ifstream file{filename.c_str()}; +#elif defined(NOWIDE_FSTREAM_INCLUDED_HPP) + nowide::ifstream file{filename.c_str()}; +#else + std::ifstream file{filename}; +#endif + if (!file.is_open()) + throw parse_exception{filename + " could not be opened for parsing"}; + parser p{file}; + return p.parse(); +} + +template <class... Ts> +struct value_accept; + +template <> +struct value_accept<> +{ + template <class Visitor, class... Args> + static void accept(const base&, Visitor&&, Args&&...) + { + // nothing + } +}; + +template <class T, class... Ts> +struct value_accept<T, Ts...> +{ + template <class Visitor, class... Args> + static void accept(const base& b, Visitor&& visitor, Args&&... args) + { + if (auto v = b.as<T>()) + { + visitor.visit(*v, std::forward<Args>(args)...); + } + else + { + value_accept<Ts...>::accept(b, std::forward<Visitor>(visitor), + std::forward<Args>(args)...); + } + } +}; + +/** + * base implementation of accept() that calls visitor.visit() on the concrete + * class. + */ +template <class Visitor, class... Args> +void base::accept(Visitor&& visitor, Args&&... args) const +{ + if (is_value()) + { + using value_acceptor + = value_accept<std::string, int64_t, double, bool, local_date, + local_time, local_datetime, offset_datetime>; + value_acceptor::accept(*this, std::forward<Visitor>(visitor), + std::forward<Args>(args)...); + } + else if (is_table()) + { + visitor.visit(static_cast<const table&>(*this), + std::forward<Args>(args)...); + } + else if (is_array()) + { + visitor.visit(static_cast<const array&>(*this), + std::forward<Args>(args)...); + } + else if (is_table_array()) + { + visitor.visit(static_cast<const table_array&>(*this), + std::forward<Args>(args)...); + } +} + +/** + * Writer that can be passed to accept() functions of cpptoml objects and + * will output valid TOML to a stream. + */ +class toml_writer +{ + public: + /** + * Construct a toml_writer that will write to the given stream + */ + toml_writer(std::ostream& s, const std::string& indent_space = "\t") + : stream_(s), indent_(indent_space), has_naked_endline_(false) + { + // nothing + } + + public: + /** + * Output a base value of the TOML tree. + */ + template <class T> + void visit(const value<T>& v, bool = false) + { + write(v); + } + + /** + * Output a table element of the TOML tree + */ + void visit(const table& t, bool in_array = false) + { + write_table_header(in_array); + std::vector<std::string> values; + std::vector<std::string> tables; + + for (const auto& i : t) + { + if (i.second->is_table() || i.second->is_table_array()) + { + tables.push_back(i.first); + } + else + { + values.push_back(i.first); + } + } + + for (unsigned int i = 0; i < values.size(); ++i) + { + path_.push_back(values[i]); + + if (i > 0) + endline(); + + write_table_item_header(*t.get(values[i])); + t.get(values[i])->accept(*this, false); + path_.pop_back(); + } + + for (unsigned int i = 0; i < tables.size(); ++i) + { + path_.push_back(tables[i]); + + if (values.size() > 0 || i > 0) + endline(); + + write_table_item_header(*t.get(tables[i])); + t.get(tables[i])->accept(*this, false); + path_.pop_back(); + } + + endline(); + } + + /** + * Output an array element of the TOML tree + */ + void visit(const array& a, bool = false) + { + write("["); + + for (unsigned int i = 0; i < a.get().size(); ++i) + { + if (i > 0) + write(", "); + + if (a.get()[i]->is_array()) + { + a.get()[i]->as_array()->accept(*this, true); + } + else + { + a.get()[i]->accept(*this, true); + } + } + + write("]"); + } + + /** + * Output a table_array element of the TOML tree + */ + void visit(const table_array& t, bool = false) + { + for (unsigned int j = 0; j < t.get().size(); ++j) + { + if (j > 0) + endline(); + + t.get()[j]->accept(*this, true); + } + + endline(); + } + + /** + * Escape a string for output. + */ + static std::string escape_string(const std::string& str) + { + std::string res; + for (auto it = str.begin(); it != str.end(); ++it) + { + if (*it == '\b') + { + res += "\\b"; + } + else if (*it == '\t') + { + res += "\\t"; + } + else if (*it == '\n') + { + res += "\\n"; + } + else if (*it == '\f') + { + res += "\\f"; + } + else if (*it == '\r') + { + res += "\\r"; + } + else if (*it == '"') + { + res += "\\\""; + } + else if (*it == '\\') + { + res += "\\\\"; + } + else if (*it >= 0x0000 && *it <= 0x001f) + { + res += "\\u"; + std::stringstream ss; + ss << std::hex << static_cast<uint32_t>(*it); + res += ss.str(); + } + else + { + res += *it; + } + } + return res; + } + + protected: + /** + * Write out a string. + */ + void write(const value<std::string>& v) + { + write("\""); + write(escape_string(v.get())); + write("\""); + } + + /** + * Write out a double. + */ + void write(const value<double>& v) + { + std::ios::fmtflags flags{stream_.flags()}; + + stream_ << std::showpoint; + write(v.get()); + + stream_.flags(flags); + } + + /** + * Write out an integer, local_date, local_time, local_datetime, or + * offset_datetime. + */ + template <class T> + typename std::enable_if<is_one_of<T, int64_t, local_date, local_time, + local_datetime, + offset_datetime>::value>::type + write(const value<T>& v) + { + write(v.get()); + } + + /** + * Write out a boolean. + */ + void write(const value<bool>& v) + { + write((v.get() ? "true" : "false")); + } + + /** + * Write out the header of a table. + */ + void write_table_header(bool in_array = false) + { + if (!path_.empty()) + { + indent(); + + write("["); + + if (in_array) + { + write("["); + } + + for (unsigned int i = 0; i < path_.size(); ++i) + { + if (i > 0) + { + write("."); + } + + if (path_[i].find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde" + "fghijklmnopqrstuvwxyz0123456789" + "_-") + == std::string::npos) + { + write(path_[i]); + } + else + { + write("\""); + write(escape_string(path_[i])); + write("\""); + } + } + + if (in_array) + { + write("]"); + } + + write("]"); + endline(); + } + } + + /** + * Write out the identifier for an item in a table. + */ + void write_table_item_header(const base& b) + { + if (!b.is_table() && !b.is_table_array()) + { + indent(); + + if (path_.back().find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde" + "fghijklmnopqrstuvwxyz0123456789" + "_-") + == std::string::npos) + { + write(path_.back()); + } + else + { + write("\""); + write(escape_string(path_.back())); + write("\""); + } + + write(" = "); + } + } + + private: + /** + * Indent the proper number of tabs given the size of + * the path. + */ + void indent() + { + for (std::size_t i = 1; i < path_.size(); ++i) + write(indent_); + } + + /** + * Write a value out to the stream. + */ + template <class T> + void write(const T& v) + { + stream_ << v; + has_naked_endline_ = false; + } + + /** + * Write an endline out to the stream + */ + void endline() + { + if (!has_naked_endline_) + { + stream_ << "\n"; + has_naked_endline_ = true; + } + } + + private: + std::ostream& stream_; + const std::string indent_; + std::vector<std::string> path_; + bool has_naked_endline_; +}; + +inline std::ostream& operator<<(std::ostream& stream, const base& b) +{ + toml_writer writer{stream}; + b.accept(writer); + return stream; +} + +template <class T> +std::ostream& operator<<(std::ostream& stream, const value<T>& v) +{ + toml_writer writer{stream}; + v.accept(writer); + return stream; +} + +inline std::ostream& operator<<(std::ostream& stream, const table& t) +{ + toml_writer writer{stream}; + t.accept(writer); + return stream; +} + +inline std::ostream& operator<<(std::ostream& stream, const table_array& t) +{ + toml_writer writer{stream}; + t.accept(writer); + return stream; +} + +inline std::ostream& operator<<(std::ostream& stream, const array& a) +{ + toml_writer writer{stream}; + a.accept(writer); + return stream; +} +} +#endif diff --git a/package/hvac/bin/runxdg b/package/hvac/bin/runxdg new file mode 100755 index 0000000..dac903c --- /dev/null +++ b/package/hvac/bin/runxdg @@ -0,0 +1,2 @@ +#!/bin/sh +exec /usr/bin/runxdg $@ diff --git a/package/hvac/config.xml b/package/hvac/config.xml new file mode 100644 index 0000000..d6722bd --- /dev/null +++ b/package/hvac/config.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<widget xmlns="http://www.w3.org/ns/widgets" id="hvac" version="0.1"> + <name>HVAC</name> + <icon src="icon.svg"/> + <content src="bin/runxdg" type="application/vnd.agl.native"/> + <description>Launcher for XDG application on AGL HomeScreen 2017</description> + <author>Panasonic Corporation</author> + <license>MIT</license> + <feature name="urn:AGL:widget:required-api"> + <param name="homescreen" value="ws" /> + <param name="windowmanager" value="ws" /> + </feature> + <feature name="urn:AGL:widget:required-permission"> + <param name="urn:AGL:permission::public:no-htdocs" value="required" /> + <param name="http://tizen.org/privilege/internal/dbus" value="required" /> + </feature> +</widget> diff --git a/package/hvac/icon.svg b/package/hvac/icon.svg new file mode 100644 index 0000000..91661a7 --- /dev/null +++ b/package/hvac/icon.svg @@ -0,0 +1,279 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> + +<svg + xmlns:i="&ns_ai;" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + x="0px" + y="0px" + viewBox="0 0 320 320" + style="enable-background:new 0 0 320 320;" + xml:space="preserve" + id="svg2" + inkscape:version="0.91 r13725" + sodipodi:docname="icon.svg"><metadata + id="metadata1292"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs1290" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="2560" + inkscape:window-height="1464" + id="namedview1288" + showgrid="false" + inkscape:zoom="0.7375" + inkscape:cx="-572.20339" + inkscape:cy="160" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="svg2" /><style + type="text/css" + id="style4"> + .st0{display:none;} + .st1{display:inline;} + .st2{opacity:0.4;fill:url(#SVGID_1_);} + .st3{fill:url(#SVGID_2_);} + .st4{fill:#FFFFFF;} + .st5{font-family:'Roboto-Regular';} + .st6{font-size:25px;} + .st7{letter-spacing:6;} + .st8{fill:url(#SVGID_3_);} + .st9{fill:url(#SVGID_4_);} + .st10{fill:url(#SVGID_5_);} + .st11{fill:url(#SVGID_6_);} + .st12{fill:url(#SVGID_7_);} + .st13{fill:url(#SVGID_8_);} + .st14{fill:url(#SVGID_9_);} + .st15{fill:url(#SVGID_10_);} + .st16{fill:url(#SVGID_11_);} + .st17{fill:url(#SVGID_12_);} + .st18{fill:url(#SVGID_13_);} + .st19{fill:url(#SVGID_14_);} + .st20{fill:url(#SVGID_15_);} + .st21{fill:url(#SVGID_16_);} + .st22{fill:url(#SVGID_17_);} + .st23{fill:url(#SVGID_18_);} + .st24{opacity:0.29;} + .st25{fill:url(#SVGID_19_);} + .st26{fill:url(#SVGID_20_);} + .st27{fill:url(#SVGID_21_);} + .st28{fill:url(#SVGID_22_);} + .st29{fill:url(#SVGID_23_);} + .st30{fill:url(#SVGID_24_);} + .st31{fill:url(#SVGID_25_);} + .st32{fill:url(#SVGID_26_);} + .st33{fill:url(#SVGID_27_);} + .st34{fill:url(#SVGID_28_);} + .st35{fill:url(#SVGID_29_);} + .st36{fill:url(#SVGID_30_);} + .st37{fill:url(#SVGID_31_);} + .st38{fill:url(#SVGID_32_);} + .st39{fill:url(#SVGID_33_);} + .st40{fill:url(#SVGID_34_);} + .st41{fill:url(#SVGID_35_);} + .st42{fill:url(#SVGID_36_);} + .st43{opacity:0.4;fill:url(#SVGID_37_);} + .st44{fill:url(#SVGID_38_);} + .st45{fill:url(#SVGID_39_);} + .st46{fill:url(#SVGID_40_);} + .st47{fill:url(#SVGID_41_);} + .st48{fill:url(#SVGID_42_);} + .st49{fill:url(#SVGID_43_);} + .st50{fill:url(#SVGID_44_);} + .st51{display:inline;opacity:0.29;} + .st52{display:inline;fill:url(#SVGID_45_);} + .st53{display:inline;fill:url(#SVGID_46_);} + .st54{display:inline;fill:#FFFFFF;} + .st55{display:inline;fill:url(#SVGID_47_);} + .st56{display:inline;fill:url(#SVGID_48_);} + .st57{display:inline;fill:url(#SVGID_49_);} + .st58{display:inline;fill:url(#SVGID_50_);} + .st59{display:inline;fill:url(#SVGID_51_);} + .st60{display:inline;fill:url(#SVGID_52_);} + .st61{opacity:0.4;fill:url(#SVGID_53_);} + .st62{fill:url(#SVGID_54_);} + .st63{fill:url(#SVGID_55_);} + .st64{fill:url(#SVGID_56_);} + .st65{fill:url(#SVGID_57_);} + .st66{fill:url(#SVGID_58_);} + .st67{opacity:0.4;fill:url(#SVGID_59_);} + .st68{fill:url(#SVGID_60_);} + .st69{fill:url(#SVGID_61_);} + .st70{fill:url(#SVGID_62_);} + .st71{fill:url(#SVGID_63_);} + .st72{fill:url(#SVGID_64_);} + .st73{fill:url(#SVGID_65_);} + .st74{fill:url(#SVGID_66_);} + .st75{fill:url(#SVGID_67_);} + .st76{fill:url(#SVGID_68_);} + .st77{fill:url(#SVGID_69_);} + .st78{fill:url(#SVGID_70_);} + .st79{fill:url(#SVGID_71_);} + .st80{fill:url(#SVGID_72_);} + .st81{fill:url(#SVGID_73_);} + .st82{fill:url(#SVGID_74_);} + .st83{fill:url(#SVGID_75_);} + .st84{fill:url(#SVGID_76_);} + .st85{fill:url(#SVGID_77_);} + .st86{fill:url(#SVGID_78_);} + .st87{fill:url(#SVGID_79_);} + .st88{fill:url(#SVGID_80_);} + .st89{fill:url(#SVGID_81_);} + .st90{fill:url(#SVGID_82_);} + .st91{fill:url(#SVGID_83_);} + .st92{fill:url(#SVGID_84_);} + .st93{fill:url(#SVGID_85_);} + .st94{fill:url(#SVGID_86_);} + .st95{opacity:0.4;fill:url(#SVGID_87_);} + .st96{fill:url(#SVGID_88_);} + .st97{fill:url(#SVGID_89_);} + .st98{fill:url(#SVGID_90_);} + .st99{fill:url(#SVGID_91_);} + .st100{fill:url(#SVGID_92_);} + .st101{fill:url(#SVGID_93_);} + .st102{fill:url(#SVGID_94_);} + .st103{opacity:0.4;fill:url(#SVGID_95_);} + .st104{fill:url(#SVGID_96_);} + .st105{fill:url(#SVGID_97_);} + .st106{fill:url(#SVGID_98_);} + .st107{fill:url(#SVGID_99_);} + .st108{fill:url(#SVGID_100_);} + .st109{fill:url(#SVGID_101_);} + .st110{display:inline;fill:url(#SVGID_102_);} + .st111{display:inline;fill:url(#SVGID_103_);} + .st112{fill:url(#SVGID_104_);} + .st113{fill:url(#SVGID_105_);} + .st114{fill:url(#SVGID_106_);} + .st115{fill:url(#SVGID_107_);} + .st116{fill:url(#SVGID_108_);} + .st117{opacity:0.4;fill:url(#SVGID_109_);} + .st118{fill:url(#SVGID_110_);} + .st119{fill:url(#SVGID_111_);} + .st120{fill:url(#SVGID_112_);} + .st121{fill:url(#SVGID_113_);} + .st122{fill:url(#SVGID_114_);} + .st123{opacity:0.4;fill:url(#SVGID_115_);} + .st124{fill:url(#SVGID_116_);} + .st125{fill:url(#SVGID_117_);} + .st126{fill:url(#SVGID_118_);} + .st127{display:inline;fill:url(#SVGID_119_);} + .st128{display:inline;fill:url(#SVGID_120_);} + .st129{fill:url(#SVGID_121_);} + .st130{fill:url(#SVGID_122_);} +</style><switch + id="switch6"><g + i:extraneous="self" + id="g8"><g + id="Multimedia_Inactive_copy"><circle + class="st24" + cx="159.7" + cy="133.4" + r="101.9" + id="circle884" /><linearGradient + id="SVGID_91_" + gradientUnits="userSpaceOnUse" + x1="115.9317" + y1="254.1836" + x2="256.3852" + y2="-133.5267"><stop + offset="0" + style="stop-color:#8BC53F" + id="stop887" /><stop + offset="2.015080e-02" + style="stop-color:#7CCB56;stop-opacity:0.9678" + id="stop889" /><stop + offset="6.089833e-02" + style="stop-color:#62D67D;stop-opacity:0.9028" + id="stop891" /><stop + offset="0.1057" + style="stop-color:#4BDFA0;stop-opacity:0.8312" + id="stop893" /><stop + offset="0.1543" + style="stop-color:#38E7BE;stop-opacity:0.7537" + id="stop895" /><stop + offset="0.2077" + style="stop-color:#28EED6;stop-opacity:0.6684" + id="stop897" /><stop + offset="0.2681" + style="stop-color:#1CF3E8;stop-opacity:0.572" + id="stop899" /><stop + offset="0.3394" + style="stop-color:#13F6F5;stop-opacity:0.4581" + id="stop901" /><stop + offset="0.4323" + style="stop-color:#0EF8FD;stop-opacity:0.3098" + id="stop903" /><stop + offset="0.6264" + style="stop-color:#0DF9FF;stop-opacity:0" + id="stop905" /></linearGradient><circle + class="st99" + cx="159.7" + cy="133.4" + r="101.9" + id="circle907" /><linearGradient + id="SVGID_92_" + gradientUnits="userSpaceOnUse" + x1="4.0481" + y1="287.9492" + x2="320.4859" + y2="-15.4029" + gradientTransform="matrix(1 5.464556e-03 -5.464556e-03 1 -2.0192 -3.0212)"><stop + offset="0" + style="stop-color:#59FF7F" + id="stop910" /><stop + offset="1" + style="stop-color:#6BFBFF" + id="stop912" /></linearGradient><path + class="st100" + d="M160,238.8c-0.2,0-0.4,0-0.6,0c-58-0.3-104.9-47.7-104.6-105.7C55.2,75.3,102.3,28.5,160,28.5 c0.2,0,0.4,0,0.6,0c58,0.3,104.9,47.7,104.6,105.7l0,0C264.8,192,217.7,238.8,160,238.8z M160,32.2 c-55.7,0-101.2,45.2-101.5,100.9c-0.3,55.9,45,101.7,100.9,102c0.2,0,0.4,0,0.6,0c55.7,0,101.2-45.2,101.5-100.9 c0.3-55.9-45-101.7-100.9-102C160.4,32.2,160.2,32.2,160,32.2z" + id="path914" /><g + id="g916"><text + transform="matrix(1 0 0 1 53.5841 284.7119)" + class="st4 st5 st6 st7" + id="text918">MULTIMEDIA</text> +<linearGradient + id="SVGID_93_" + gradientUnits="userSpaceOnUse" + x1="140.5445" + y1="202.2363" + x2="186.8444" + y2="68.7049"><stop + offset="0" + style="stop-color:#59FF7F" + id="stop921" /><stop + offset="1" + style="stop-color:#6BFBFF" + id="stop923" /></linearGradient><path + class="st101" + d="M114.5,190.9c-6.4,0-12-2.6-14.8-7.5c-2.9-4.9-5.4-14.5,9.6-23.2c4.8-2.8,17.1-3.9,20.8-4l0.1,3.6 c-4.6,0.1-15.5,1.4-19.1,3.5c-9.4,5.4-12.1,11.5-8.3,18.3c3.8,6.6,14.6,7.6,24,2.2c6.6-3.8,10.6-10.5,10.7-17.9l-0.1-0.7V95.4 l71.9-14.2l0.1,71.3c0,6.7-3.3,16.4-12.5,21.8c-11.1,6.4-24.1,4.8-28.9-3.5c-2.9-4.9-5.4-14.5,9.6-23.2 c4.4-2.5,14.4-3.8,18.8-3.9l0.1,3.6c-4.2,0.1-13.5,1.4-17.1,3.5c-6.4,3.7-13.1,9.9-8.3,18.3c3.8,6.6,14.6,7.6,24,2.2 c7.9-4.5,10.7-12.8,10.7-18.5l-0.1-0.8V85.6l-64.7,12.7v66.8l0.1,0.7c0,8.7-4.7,16.6-12.5,21.1 C123.9,189.6,119,190.9,114.5,190.9z" + id="path925" /><linearGradient + id="SVGID_94_" + gradientUnits="userSpaceOnUse" + x1="145.3286" + y1="203.8951" + x2="191.6285" + y2="70.3637"><stop + offset="0" + style="stop-color:#59FF7F" + id="stop928" /><stop + offset="1" + style="stop-color:#6BFBFF" + id="stop930" /></linearGradient><polygon + class="st102" + points="155.6,123.3 154.8,119.8 195.5,110.2 196.3,113.7 " + id="polygon932" /></g></g></g></switch></svg>
\ No newline at end of file diff --git a/package/hvac/runxdg.toml b/package/hvac/runxdg.toml new file mode 100644 index 0000000..243db32 --- /dev/null +++ b/package/hvac/runxdg.toml @@ -0,0 +1,22 @@ +[application] +# role: identifier for WindowManager (used in layers.json) +# e.g. role = "WebBrowser" +role = "HVAC" + +# launch by "POSIX"(fork/exec), "AFM_DBUS"(afm via dbus), "AFM_WEBSOCKET"(afm via websockt) +method = "POSIX" + +# path: path to the executable +# e.g. +# path = "/usr/bin/chromium" +path = "/usr/bin/weston-simple-egl" + +# params: arguments of the excecutable +# e.g. +# params = [ +# "--mus", +# "--no-sandbox", +# '--window-size="1080,1488"', +# "--ozone-platform=wayland", +# "<URL>" +# ] diff --git a/package/navi/bin/runxdg b/package/navi/bin/runxdg new file mode 100755 index 0000000..dac903c --- /dev/null +++ b/package/navi/bin/runxdg @@ -0,0 +1,2 @@ +#!/bin/sh +exec /usr/bin/runxdg $@ diff --git a/package/navi/config.xml b/package/navi/config.xml new file mode 100644 index 0000000..97c921b --- /dev/null +++ b/package/navi/config.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<widget xmlns="http://www.w3.org/ns/widgets" id="navigation" version="0.1"> + <name>Navigation</name> + <icon src="icon.svg"/> + <content src="bin/runxdg" type="application/vnd.agl.native"/> + <description>Launcher for XDG application on AGL HomeScreen 2017</description> + <author>Panasonic Corporation</author> + <license>MIT</license> + <feature name="urn:AGL:widget:required-api"> + <param name="homescreen" value="ws" /> + <param name="windowmanager" value="ws" /> + </feature> + <feature name="urn:AGL:widget:required-permission"> + <param name="urn:AGL:permission::public:no-htdocs" value="required" /> + <param name="http://tizen.org/privilege/internal/dbus" value="required" /> + </feature> +</widget> diff --git a/package/navi/icon.svg b/package/navi/icon.svg new file mode 100644 index 0000000..91661a7 --- /dev/null +++ b/package/navi/icon.svg @@ -0,0 +1,279 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> + +<svg + xmlns:i="&ns_ai;" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + x="0px" + y="0px" + viewBox="0 0 320 320" + style="enable-background:new 0 0 320 320;" + xml:space="preserve" + id="svg2" + inkscape:version="0.91 r13725" + sodipodi:docname="icon.svg"><metadata + id="metadata1292"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs1290" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="2560" + inkscape:window-height="1464" + id="namedview1288" + showgrid="false" + inkscape:zoom="0.7375" + inkscape:cx="-572.20339" + inkscape:cy="160" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="svg2" /><style + type="text/css" + id="style4"> + .st0{display:none;} + .st1{display:inline;} + .st2{opacity:0.4;fill:url(#SVGID_1_);} + .st3{fill:url(#SVGID_2_);} + .st4{fill:#FFFFFF;} + .st5{font-family:'Roboto-Regular';} + .st6{font-size:25px;} + .st7{letter-spacing:6;} + .st8{fill:url(#SVGID_3_);} + .st9{fill:url(#SVGID_4_);} + .st10{fill:url(#SVGID_5_);} + .st11{fill:url(#SVGID_6_);} + .st12{fill:url(#SVGID_7_);} + .st13{fill:url(#SVGID_8_);} + .st14{fill:url(#SVGID_9_);} + .st15{fill:url(#SVGID_10_);} + .st16{fill:url(#SVGID_11_);} + .st17{fill:url(#SVGID_12_);} + .st18{fill:url(#SVGID_13_);} + .st19{fill:url(#SVGID_14_);} + .st20{fill:url(#SVGID_15_);} + .st21{fill:url(#SVGID_16_);} + .st22{fill:url(#SVGID_17_);} + .st23{fill:url(#SVGID_18_);} + .st24{opacity:0.29;} + .st25{fill:url(#SVGID_19_);} + .st26{fill:url(#SVGID_20_);} + .st27{fill:url(#SVGID_21_);} + .st28{fill:url(#SVGID_22_);} + .st29{fill:url(#SVGID_23_);} + .st30{fill:url(#SVGID_24_);} + .st31{fill:url(#SVGID_25_);} + .st32{fill:url(#SVGID_26_);} + .st33{fill:url(#SVGID_27_);} + .st34{fill:url(#SVGID_28_);} + .st35{fill:url(#SVGID_29_);} + .st36{fill:url(#SVGID_30_);} + .st37{fill:url(#SVGID_31_);} + .st38{fill:url(#SVGID_32_);} + .st39{fill:url(#SVGID_33_);} + .st40{fill:url(#SVGID_34_);} + .st41{fill:url(#SVGID_35_);} + .st42{fill:url(#SVGID_36_);} + .st43{opacity:0.4;fill:url(#SVGID_37_);} + .st44{fill:url(#SVGID_38_);} + .st45{fill:url(#SVGID_39_);} + .st46{fill:url(#SVGID_40_);} + .st47{fill:url(#SVGID_41_);} + .st48{fill:url(#SVGID_42_);} + .st49{fill:url(#SVGID_43_);} + .st50{fill:url(#SVGID_44_);} + .st51{display:inline;opacity:0.29;} + .st52{display:inline;fill:url(#SVGID_45_);} + .st53{display:inline;fill:url(#SVGID_46_);} + .st54{display:inline;fill:#FFFFFF;} + .st55{display:inline;fill:url(#SVGID_47_);} + .st56{display:inline;fill:url(#SVGID_48_);} + .st57{display:inline;fill:url(#SVGID_49_);} + .st58{display:inline;fill:url(#SVGID_50_);} + .st59{display:inline;fill:url(#SVGID_51_);} + .st60{display:inline;fill:url(#SVGID_52_);} + .st61{opacity:0.4;fill:url(#SVGID_53_);} + .st62{fill:url(#SVGID_54_);} + .st63{fill:url(#SVGID_55_);} + .st64{fill:url(#SVGID_56_);} + .st65{fill:url(#SVGID_57_);} + .st66{fill:url(#SVGID_58_);} + .st67{opacity:0.4;fill:url(#SVGID_59_);} + .st68{fill:url(#SVGID_60_);} + .st69{fill:url(#SVGID_61_);} + .st70{fill:url(#SVGID_62_);} + .st71{fill:url(#SVGID_63_);} + .st72{fill:url(#SVGID_64_);} + .st73{fill:url(#SVGID_65_);} + .st74{fill:url(#SVGID_66_);} + .st75{fill:url(#SVGID_67_);} + .st76{fill:url(#SVGID_68_);} + .st77{fill:url(#SVGID_69_);} + .st78{fill:url(#SVGID_70_);} + .st79{fill:url(#SVGID_71_);} + .st80{fill:url(#SVGID_72_);} + .st81{fill:url(#SVGID_73_);} + .st82{fill:url(#SVGID_74_);} + .st83{fill:url(#SVGID_75_);} + .st84{fill:url(#SVGID_76_);} + .st85{fill:url(#SVGID_77_);} + .st86{fill:url(#SVGID_78_);} + .st87{fill:url(#SVGID_79_);} + .st88{fill:url(#SVGID_80_);} + .st89{fill:url(#SVGID_81_);} + .st90{fill:url(#SVGID_82_);} + .st91{fill:url(#SVGID_83_);} + .st92{fill:url(#SVGID_84_);} + .st93{fill:url(#SVGID_85_);} + .st94{fill:url(#SVGID_86_);} + .st95{opacity:0.4;fill:url(#SVGID_87_);} + .st96{fill:url(#SVGID_88_);} + .st97{fill:url(#SVGID_89_);} + .st98{fill:url(#SVGID_90_);} + .st99{fill:url(#SVGID_91_);} + .st100{fill:url(#SVGID_92_);} + .st101{fill:url(#SVGID_93_);} + .st102{fill:url(#SVGID_94_);} + .st103{opacity:0.4;fill:url(#SVGID_95_);} + .st104{fill:url(#SVGID_96_);} + .st105{fill:url(#SVGID_97_);} + .st106{fill:url(#SVGID_98_);} + .st107{fill:url(#SVGID_99_);} + .st108{fill:url(#SVGID_100_);} + .st109{fill:url(#SVGID_101_);} + .st110{display:inline;fill:url(#SVGID_102_);} + .st111{display:inline;fill:url(#SVGID_103_);} + .st112{fill:url(#SVGID_104_);} + .st113{fill:url(#SVGID_105_);} + .st114{fill:url(#SVGID_106_);} + .st115{fill:url(#SVGID_107_);} + .st116{fill:url(#SVGID_108_);} + .st117{opacity:0.4;fill:url(#SVGID_109_);} + .st118{fill:url(#SVGID_110_);} + .st119{fill:url(#SVGID_111_);} + .st120{fill:url(#SVGID_112_);} + .st121{fill:url(#SVGID_113_);} + .st122{fill:url(#SVGID_114_);} + .st123{opacity:0.4;fill:url(#SVGID_115_);} + .st124{fill:url(#SVGID_116_);} + .st125{fill:url(#SVGID_117_);} + .st126{fill:url(#SVGID_118_);} + .st127{display:inline;fill:url(#SVGID_119_);} + .st128{display:inline;fill:url(#SVGID_120_);} + .st129{fill:url(#SVGID_121_);} + .st130{fill:url(#SVGID_122_);} +</style><switch + id="switch6"><g + i:extraneous="self" + id="g8"><g + id="Multimedia_Inactive_copy"><circle + class="st24" + cx="159.7" + cy="133.4" + r="101.9" + id="circle884" /><linearGradient + id="SVGID_91_" + gradientUnits="userSpaceOnUse" + x1="115.9317" + y1="254.1836" + x2="256.3852" + y2="-133.5267"><stop + offset="0" + style="stop-color:#8BC53F" + id="stop887" /><stop + offset="2.015080e-02" + style="stop-color:#7CCB56;stop-opacity:0.9678" + id="stop889" /><stop + offset="6.089833e-02" + style="stop-color:#62D67D;stop-opacity:0.9028" + id="stop891" /><stop + offset="0.1057" + style="stop-color:#4BDFA0;stop-opacity:0.8312" + id="stop893" /><stop + offset="0.1543" + style="stop-color:#38E7BE;stop-opacity:0.7537" + id="stop895" /><stop + offset="0.2077" + style="stop-color:#28EED6;stop-opacity:0.6684" + id="stop897" /><stop + offset="0.2681" + style="stop-color:#1CF3E8;stop-opacity:0.572" + id="stop899" /><stop + offset="0.3394" + style="stop-color:#13F6F5;stop-opacity:0.4581" + id="stop901" /><stop + offset="0.4323" + style="stop-color:#0EF8FD;stop-opacity:0.3098" + id="stop903" /><stop + offset="0.6264" + style="stop-color:#0DF9FF;stop-opacity:0" + id="stop905" /></linearGradient><circle + class="st99" + cx="159.7" + cy="133.4" + r="101.9" + id="circle907" /><linearGradient + id="SVGID_92_" + gradientUnits="userSpaceOnUse" + x1="4.0481" + y1="287.9492" + x2="320.4859" + y2="-15.4029" + gradientTransform="matrix(1 5.464556e-03 -5.464556e-03 1 -2.0192 -3.0212)"><stop + offset="0" + style="stop-color:#59FF7F" + id="stop910" /><stop + offset="1" + style="stop-color:#6BFBFF" + id="stop912" /></linearGradient><path + class="st100" + d="M160,238.8c-0.2,0-0.4,0-0.6,0c-58-0.3-104.9-47.7-104.6-105.7C55.2,75.3,102.3,28.5,160,28.5 c0.2,0,0.4,0,0.6,0c58,0.3,104.9,47.7,104.6,105.7l0,0C264.8,192,217.7,238.8,160,238.8z M160,32.2 c-55.7,0-101.2,45.2-101.5,100.9c-0.3,55.9,45,101.7,100.9,102c0.2,0,0.4,0,0.6,0c55.7,0,101.2-45.2,101.5-100.9 c0.3-55.9-45-101.7-100.9-102C160.4,32.2,160.2,32.2,160,32.2z" + id="path914" /><g + id="g916"><text + transform="matrix(1 0 0 1 53.5841 284.7119)" + class="st4 st5 st6 st7" + id="text918">MULTIMEDIA</text> +<linearGradient + id="SVGID_93_" + gradientUnits="userSpaceOnUse" + x1="140.5445" + y1="202.2363" + x2="186.8444" + y2="68.7049"><stop + offset="0" + style="stop-color:#59FF7F" + id="stop921" /><stop + offset="1" + style="stop-color:#6BFBFF" + id="stop923" /></linearGradient><path + class="st101" + d="M114.5,190.9c-6.4,0-12-2.6-14.8-7.5c-2.9-4.9-5.4-14.5,9.6-23.2c4.8-2.8,17.1-3.9,20.8-4l0.1,3.6 c-4.6,0.1-15.5,1.4-19.1,3.5c-9.4,5.4-12.1,11.5-8.3,18.3c3.8,6.6,14.6,7.6,24,2.2c6.6-3.8,10.6-10.5,10.7-17.9l-0.1-0.7V95.4 l71.9-14.2l0.1,71.3c0,6.7-3.3,16.4-12.5,21.8c-11.1,6.4-24.1,4.8-28.9-3.5c-2.9-4.9-5.4-14.5,9.6-23.2 c4.4-2.5,14.4-3.8,18.8-3.9l0.1,3.6c-4.2,0.1-13.5,1.4-17.1,3.5c-6.4,3.7-13.1,9.9-8.3,18.3c3.8,6.6,14.6,7.6,24,2.2 c7.9-4.5,10.7-12.8,10.7-18.5l-0.1-0.8V85.6l-64.7,12.7v66.8l0.1,0.7c0,8.7-4.7,16.6-12.5,21.1 C123.9,189.6,119,190.9,114.5,190.9z" + id="path925" /><linearGradient + id="SVGID_94_" + gradientUnits="userSpaceOnUse" + x1="145.3286" + y1="203.8951" + x2="191.6285" + y2="70.3637"><stop + offset="0" + style="stop-color:#59FF7F" + id="stop928" /><stop + offset="1" + style="stop-color:#6BFBFF" + id="stop930" /></linearGradient><polygon + class="st102" + points="155.6,123.3 154.8,119.8 195.5,110.2 196.3,113.7 " + id="polygon932" /></g></g></g></switch></svg>
\ No newline at end of file diff --git a/package/navi/runxdg.toml b/package/navi/runxdg.toml new file mode 100644 index 0000000..9af35ac --- /dev/null +++ b/package/navi/runxdg.toml @@ -0,0 +1,22 @@ +[application] +# role: identifier for WindowManager (used in layers.json) +# e.g. role = "WebBrowser" +role = "Navigation" + +# launch by "POSIX"(fork/exec), "AFM_DBUS"(afm via dbus), "AFM_WEBSOCKET"(afm via websockt) +method = "POSIX" + +# path: path to the executable +# e.g. +# path = "/usr/bin/chromium" +path = "/usr/bin/weston-simple-egl" + +# params: arguments of the excecutable +# e.g. +# params = [ +# "--mus", +# "--no-sandbox", +# '--window-size="1080,1488"', +# "--ozone-platform=wayland", +# "<URL>" +# ] diff --git a/package/simple-egl/bin/runxdg b/package/simple-egl/bin/runxdg new file mode 100755 index 0000000..dac903c --- /dev/null +++ b/package/simple-egl/bin/runxdg @@ -0,0 +1,2 @@ +#!/bin/sh +exec /usr/bin/runxdg $@ diff --git a/package/simple-egl/config.xml b/package/simple-egl/config.xml new file mode 100644 index 0000000..dbf701a --- /dev/null +++ b/package/simple-egl/config.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<widget xmlns="http://www.w3.org/ns/widgets" id="runxdg" version="0.1"> + <name>SimpleEGL</name> + <icon src="icon.svg"/> + <content src="bin/runxdg" type="application/vnd.agl.native"/> + <description>Launcher for XDG application on AGL HomeScreen 2017</description> + <author>Panasonic Corporation</author> + <license>MIT</license> + <feature name="urn:AGL:widget:required-api"> + <param name="homescreen" value="ws" /> + <param name="windowmanager" value="ws" /> + </feature> + <feature name="urn:AGL:widget:required-permission"> + <param name="urn:AGL:permission::public:no-htdocs" value="required" /> + <param name="http://tizen.org/privilege/internal/dbus" value="required" /> + </feature> +</widget> diff --git a/package/simple-egl/icon.svg b/package/simple-egl/icon.svg new file mode 100644 index 0000000..91661a7 --- /dev/null +++ b/package/simple-egl/icon.svg @@ -0,0 +1,279 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> + +<svg + xmlns:i="&ns_ai;" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + x="0px" + y="0px" + viewBox="0 0 320 320" + style="enable-background:new 0 0 320 320;" + xml:space="preserve" + id="svg2" + inkscape:version="0.91 r13725" + sodipodi:docname="icon.svg"><metadata + id="metadata1292"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs1290" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="2560" + inkscape:window-height="1464" + id="namedview1288" + showgrid="false" + inkscape:zoom="0.7375" + inkscape:cx="-572.20339" + inkscape:cy="160" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="svg2" /><style + type="text/css" + id="style4"> + .st0{display:none;} + .st1{display:inline;} + .st2{opacity:0.4;fill:url(#SVGID_1_);} + .st3{fill:url(#SVGID_2_);} + .st4{fill:#FFFFFF;} + .st5{font-family:'Roboto-Regular';} + .st6{font-size:25px;} + .st7{letter-spacing:6;} + .st8{fill:url(#SVGID_3_);} + .st9{fill:url(#SVGID_4_);} + .st10{fill:url(#SVGID_5_);} + .st11{fill:url(#SVGID_6_);} + .st12{fill:url(#SVGID_7_);} + .st13{fill:url(#SVGID_8_);} + .st14{fill:url(#SVGID_9_);} + .st15{fill:url(#SVGID_10_);} + .st16{fill:url(#SVGID_11_);} + .st17{fill:url(#SVGID_12_);} + .st18{fill:url(#SVGID_13_);} + .st19{fill:url(#SVGID_14_);} + .st20{fill:url(#SVGID_15_);} + .st21{fill:url(#SVGID_16_);} + .st22{fill:url(#SVGID_17_);} + .st23{fill:url(#SVGID_18_);} + .st24{opacity:0.29;} + .st25{fill:url(#SVGID_19_);} + .st26{fill:url(#SVGID_20_);} + .st27{fill:url(#SVGID_21_);} + .st28{fill:url(#SVGID_22_);} + .st29{fill:url(#SVGID_23_);} + .st30{fill:url(#SVGID_24_);} + .st31{fill:url(#SVGID_25_);} + .st32{fill:url(#SVGID_26_);} + .st33{fill:url(#SVGID_27_);} + .st34{fill:url(#SVGID_28_);} + .st35{fill:url(#SVGID_29_);} + .st36{fill:url(#SVGID_30_);} + .st37{fill:url(#SVGID_31_);} + .st38{fill:url(#SVGID_32_);} + .st39{fill:url(#SVGID_33_);} + .st40{fill:url(#SVGID_34_);} + .st41{fill:url(#SVGID_35_);} + .st42{fill:url(#SVGID_36_);} + .st43{opacity:0.4;fill:url(#SVGID_37_);} + .st44{fill:url(#SVGID_38_);} + .st45{fill:url(#SVGID_39_);} + .st46{fill:url(#SVGID_40_);} + .st47{fill:url(#SVGID_41_);} + .st48{fill:url(#SVGID_42_);} + .st49{fill:url(#SVGID_43_);} + .st50{fill:url(#SVGID_44_);} + .st51{display:inline;opacity:0.29;} + .st52{display:inline;fill:url(#SVGID_45_);} + .st53{display:inline;fill:url(#SVGID_46_);} + .st54{display:inline;fill:#FFFFFF;} + .st55{display:inline;fill:url(#SVGID_47_);} + .st56{display:inline;fill:url(#SVGID_48_);} + .st57{display:inline;fill:url(#SVGID_49_);} + .st58{display:inline;fill:url(#SVGID_50_);} + .st59{display:inline;fill:url(#SVGID_51_);} + .st60{display:inline;fill:url(#SVGID_52_);} + .st61{opacity:0.4;fill:url(#SVGID_53_);} + .st62{fill:url(#SVGID_54_);} + .st63{fill:url(#SVGID_55_);} + .st64{fill:url(#SVGID_56_);} + .st65{fill:url(#SVGID_57_);} + .st66{fill:url(#SVGID_58_);} + .st67{opacity:0.4;fill:url(#SVGID_59_);} + .st68{fill:url(#SVGID_60_);} + .st69{fill:url(#SVGID_61_);} + .st70{fill:url(#SVGID_62_);} + .st71{fill:url(#SVGID_63_);} + .st72{fill:url(#SVGID_64_);} + .st73{fill:url(#SVGID_65_);} + .st74{fill:url(#SVGID_66_);} + .st75{fill:url(#SVGID_67_);} + .st76{fill:url(#SVGID_68_);} + .st77{fill:url(#SVGID_69_);} + .st78{fill:url(#SVGID_70_);} + .st79{fill:url(#SVGID_71_);} + .st80{fill:url(#SVGID_72_);} + .st81{fill:url(#SVGID_73_);} + .st82{fill:url(#SVGID_74_);} + .st83{fill:url(#SVGID_75_);} + .st84{fill:url(#SVGID_76_);} + .st85{fill:url(#SVGID_77_);} + .st86{fill:url(#SVGID_78_);} + .st87{fill:url(#SVGID_79_);} + .st88{fill:url(#SVGID_80_);} + .st89{fill:url(#SVGID_81_);} + .st90{fill:url(#SVGID_82_);} + .st91{fill:url(#SVGID_83_);} + .st92{fill:url(#SVGID_84_);} + .st93{fill:url(#SVGID_85_);} + .st94{fill:url(#SVGID_86_);} + .st95{opacity:0.4;fill:url(#SVGID_87_);} + .st96{fill:url(#SVGID_88_);} + .st97{fill:url(#SVGID_89_);} + .st98{fill:url(#SVGID_90_);} + .st99{fill:url(#SVGID_91_);} + .st100{fill:url(#SVGID_92_);} + .st101{fill:url(#SVGID_93_);} + .st102{fill:url(#SVGID_94_);} + .st103{opacity:0.4;fill:url(#SVGID_95_);} + .st104{fill:url(#SVGID_96_);} + .st105{fill:url(#SVGID_97_);} + .st106{fill:url(#SVGID_98_);} + .st107{fill:url(#SVGID_99_);} + .st108{fill:url(#SVGID_100_);} + .st109{fill:url(#SVGID_101_);} + .st110{display:inline;fill:url(#SVGID_102_);} + .st111{display:inline;fill:url(#SVGID_103_);} + .st112{fill:url(#SVGID_104_);} + .st113{fill:url(#SVGID_105_);} + .st114{fill:url(#SVGID_106_);} + .st115{fill:url(#SVGID_107_);} + .st116{fill:url(#SVGID_108_);} + .st117{opacity:0.4;fill:url(#SVGID_109_);} + .st118{fill:url(#SVGID_110_);} + .st119{fill:url(#SVGID_111_);} + .st120{fill:url(#SVGID_112_);} + .st121{fill:url(#SVGID_113_);} + .st122{fill:url(#SVGID_114_);} + .st123{opacity:0.4;fill:url(#SVGID_115_);} + .st124{fill:url(#SVGID_116_);} + .st125{fill:url(#SVGID_117_);} + .st126{fill:url(#SVGID_118_);} + .st127{display:inline;fill:url(#SVGID_119_);} + .st128{display:inline;fill:url(#SVGID_120_);} + .st129{fill:url(#SVGID_121_);} + .st130{fill:url(#SVGID_122_);} +</style><switch + id="switch6"><g + i:extraneous="self" + id="g8"><g + id="Multimedia_Inactive_copy"><circle + class="st24" + cx="159.7" + cy="133.4" + r="101.9" + id="circle884" /><linearGradient + id="SVGID_91_" + gradientUnits="userSpaceOnUse" + x1="115.9317" + y1="254.1836" + x2="256.3852" + y2="-133.5267"><stop + offset="0" + style="stop-color:#8BC53F" + id="stop887" /><stop + offset="2.015080e-02" + style="stop-color:#7CCB56;stop-opacity:0.9678" + id="stop889" /><stop + offset="6.089833e-02" + style="stop-color:#62D67D;stop-opacity:0.9028" + id="stop891" /><stop + offset="0.1057" + style="stop-color:#4BDFA0;stop-opacity:0.8312" + id="stop893" /><stop + offset="0.1543" + style="stop-color:#38E7BE;stop-opacity:0.7537" + id="stop895" /><stop + offset="0.2077" + style="stop-color:#28EED6;stop-opacity:0.6684" + id="stop897" /><stop + offset="0.2681" + style="stop-color:#1CF3E8;stop-opacity:0.572" + id="stop899" /><stop + offset="0.3394" + style="stop-color:#13F6F5;stop-opacity:0.4581" + id="stop901" /><stop + offset="0.4323" + style="stop-color:#0EF8FD;stop-opacity:0.3098" + id="stop903" /><stop + offset="0.6264" + style="stop-color:#0DF9FF;stop-opacity:0" + id="stop905" /></linearGradient><circle + class="st99" + cx="159.7" + cy="133.4" + r="101.9" + id="circle907" /><linearGradient + id="SVGID_92_" + gradientUnits="userSpaceOnUse" + x1="4.0481" + y1="287.9492" + x2="320.4859" + y2="-15.4029" + gradientTransform="matrix(1 5.464556e-03 -5.464556e-03 1 -2.0192 -3.0212)"><stop + offset="0" + style="stop-color:#59FF7F" + id="stop910" /><stop + offset="1" + style="stop-color:#6BFBFF" + id="stop912" /></linearGradient><path + class="st100" + d="M160,238.8c-0.2,0-0.4,0-0.6,0c-58-0.3-104.9-47.7-104.6-105.7C55.2,75.3,102.3,28.5,160,28.5 c0.2,0,0.4,0,0.6,0c58,0.3,104.9,47.7,104.6,105.7l0,0C264.8,192,217.7,238.8,160,238.8z M160,32.2 c-55.7,0-101.2,45.2-101.5,100.9c-0.3,55.9,45,101.7,100.9,102c0.2,0,0.4,0,0.6,0c55.7,0,101.2-45.2,101.5-100.9 c0.3-55.9-45-101.7-100.9-102C160.4,32.2,160.2,32.2,160,32.2z" + id="path914" /><g + id="g916"><text + transform="matrix(1 0 0 1 53.5841 284.7119)" + class="st4 st5 st6 st7" + id="text918">MULTIMEDIA</text> +<linearGradient + id="SVGID_93_" + gradientUnits="userSpaceOnUse" + x1="140.5445" + y1="202.2363" + x2="186.8444" + y2="68.7049"><stop + offset="0" + style="stop-color:#59FF7F" + id="stop921" /><stop + offset="1" + style="stop-color:#6BFBFF" + id="stop923" /></linearGradient><path + class="st101" + d="M114.5,190.9c-6.4,0-12-2.6-14.8-7.5c-2.9-4.9-5.4-14.5,9.6-23.2c4.8-2.8,17.1-3.9,20.8-4l0.1,3.6 c-4.6,0.1-15.5,1.4-19.1,3.5c-9.4,5.4-12.1,11.5-8.3,18.3c3.8,6.6,14.6,7.6,24,2.2c6.6-3.8,10.6-10.5,10.7-17.9l-0.1-0.7V95.4 l71.9-14.2l0.1,71.3c0,6.7-3.3,16.4-12.5,21.8c-11.1,6.4-24.1,4.8-28.9-3.5c-2.9-4.9-5.4-14.5,9.6-23.2 c4.4-2.5,14.4-3.8,18.8-3.9l0.1,3.6c-4.2,0.1-13.5,1.4-17.1,3.5c-6.4,3.7-13.1,9.9-8.3,18.3c3.8,6.6,14.6,7.6,24,2.2 c7.9-4.5,10.7-12.8,10.7-18.5l-0.1-0.8V85.6l-64.7,12.7v66.8l0.1,0.7c0,8.7-4.7,16.6-12.5,21.1 C123.9,189.6,119,190.9,114.5,190.9z" + id="path925" /><linearGradient + id="SVGID_94_" + gradientUnits="userSpaceOnUse" + x1="145.3286" + y1="203.8951" + x2="191.6285" + y2="70.3637"><stop + offset="0" + style="stop-color:#59FF7F" + id="stop928" /><stop + offset="1" + style="stop-color:#6BFBFF" + id="stop930" /></linearGradient><polygon + class="st102" + points="155.6,123.3 154.8,119.8 195.5,110.2 196.3,113.7 " + id="polygon932" /></g></g></g></switch></svg>
\ No newline at end of file diff --git a/package/simple-egl/runxdg.toml b/package/simple-egl/runxdg.toml new file mode 100644 index 0000000..1729766 --- /dev/null +++ b/package/simple-egl/runxdg.toml @@ -0,0 +1,22 @@ +[application] +# role: identifier for WindowManager (used in layers.json) +# e.g. role = "WebBrowser" +role = "SimpleEGL" + +# launch by "POSIX"(fork/exec), "AFM_DBUS"(afm via dbus), "AFM_WEBSOCKET"(afm via websockt) +method = "POSIX" + +# path: path to the executable +# e.g. +# path = "/usr/bin/chromium" +path = "/usr/bin/weston-simple-egl" + +# params: arguments of the excecutable +# e.g. +# params = [ +# "--mus", +# "--no-sandbox", +# '--window-size="1080,1488"', +# "--ozone-platform=wayland", +# "<URL>" +# ] diff --git a/src/runxdg.cpp b/src/runxdg.cpp new file mode 100644 index 0000000..5553ff5 --- /dev/null +++ b/src/runxdg.cpp @@ -0,0 +1,621 @@ +/* + * Copyright (c) 2017 Panasonic Corporation + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/wait.h> + +#include <cstdio> + +#include "cpptoml/cpptoml.h" + +#include "runxdg.hpp" + +#define RUNXDG_CONFIG "runxdg.toml" + +void fatal(const char* format, ...) +{ + va_list va_args; + va_start(va_args, format); + vfprintf(stderr, format, va_args); + va_end(va_args); + + exit(EXIT_FAILURE); +} + +void warn(const char* format, ...) +{ + va_list va_args; + va_start(va_args, format); + vfprintf(stderr, format, va_args); + va_end(va_args); +} + +void debug(const char* format, ...) +{ + va_list va_args; + va_start(va_args, format); + vfprintf(stderr, format, va_args); + va_end(va_args); +} + +void RunXDG::notify_ivi_control_cb (ilmObjectType object, t_ilm_uint id, + t_ilm_bool created) +{ + if (object == ILM_SURFACE) { + struct ilmSurfaceProperties surf_props; + + ilm_getPropertiesOfSurface(id, &surf_props); + pid_t surf_pid = surf_props.creatorPid; + + if (!created) { + AGL_DEBUG("ivi surface (id=%d, pid=%d) destroyed.", id, surf_pid); + m_launcher->unregister_surfpid(surf_pid); + m_surfaces.erase(surf_pid); + return; + } + + AGL_DEBUG("ivi surface (id=%d, pid=%d) is created.", id, surf_pid); + + m_launcher->register_surfpid(surf_pid); + if (m_launcher->m_rid && + surf_pid == m_launcher->find_surfpid_by_rid(m_launcher->m_rid)) { + setup_surface(id); + } + m_surfaces[surf_pid] = id; + } else if (object == ILM_LAYER) { + if (created) + AGL_DEBUG("ivi layer: %d created.", id); + else + AGL_DEBUG("ivi layer: %d destroyed.", id); + } +} + +void RunXDG::notify_ivi_control_cb_static (ilmObjectType object, t_ilm_uint id, + t_ilm_bool created, void *user_data) +{ + RunXDG *runxdg = static_cast<RunXDG*>(user_data); + runxdg->notify_ivi_control_cb(object, id, created); +} + +int POSIXLauncher::launch (std::string& name) +{ + pid_t pid; + + pid = fork(); + if (pid < 0) { + AGL_DEBUG("cannot fork()"); + return -1; + } + + if (pid == 0) { + // child + const char **argv = new const char * [m_args_v.size() + 1]; + for (unsigned int i = 0; i < m_args_v.size(); ++i) { + argv[i] = m_args_v[i].c_str(); + } + argv[m_args_v.size()] = NULL; + + execv(argv[0], (char **)argv); + + AGL_FATAL("fail to execve(%s)", argv[0]); + } + // parent + + return pid; +} + +void POSIXLauncher::loop (volatile sig_atomic_t& e_flag) +{ + int status; + pid_t ret; + + while (!e_flag) { + ret = waitpid(m_rid, &status, 0); + if (ret < 0) { + if (errno == EINTR) { + AGL_DEBUG("catch EINTR while waitpid()"); + continue; + } + break; + } + } + + if (ret > 0) { + if (WIFEXITED(status)) { + AGL_DEBUG("%s terminated, return %d", m_args_v[0].c_str(), + WEXITSTATUS(status)); + } + if (WIFSIGNALED(status)) { + AGL_DEBUG("%s terminated by signal %d", m_args_v[0].c_str(), + WTERMSIG(status)); + } + } + + if (e_flag) { + /* parent killed by someone, so need to kill children */ + AGL_DEBUG("killpg(0, SIGTERM)"); + killpg(0, SIGTERM); + } +} + +int AFMDBusLauncher::get_dbus_message_bus (GBusType bus_type, + GDBusConnection * &conn) +{ + GError* err = NULL; + + conn = g_bus_get_sync(bus_type, NULL, &err); + if (err) { + AGL_WARN("Failed to get session bus: %s", err->message); + g_clear_error (&err); + return -1; + } + + return 0; +} + +int AFMDBusLauncher::launch (std::string &name) +{ + GDBusMessage* msg; + GDBusMessage* re; + GDBusConnection* conn; + GError* err = NULL; + GVariant* body; + char* val; + const char* xdg_app = name.c_str(); + + if (get_dbus_message_bus(G_BUS_TYPE_SESSION, conn)) { + return -1; + } + + msg = g_dbus_message_new_method_call ( + DBUS_SERVICE, + DBUS_PATH, + DBUS_INTERFACE, + "start"); + + if (msg == NULL) { + AGL_WARN("Failed to allocate the dbus message"); + g_object_unref(conn); + return -1; + } + + g_dbus_message_set_body(msg, g_variant_new("(s)", xdg_app)); + + re = g_dbus_connection_send_message_with_reply_sync ( + conn, msg, G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, NULL, NULL, &err); + + if (err != NULL) { + AGL_WARN("unable to send message: %s", err->message); + g_clear_error(&err); + g_object_unref(conn); + g_object_unref(msg); + return -1; + } + + g_dbus_connection_flush_sync(conn, NULL, &err); + if (err != NULL) { + AGL_WARN("unable to flush message queue: %s", err->message); + g_object_unref(conn); + g_object_unref(msg); + g_object_unref(re); + return -1; + } + + body = g_dbus_message_get_body(re); + g_variant_get(body, "(&s)", &val); + + AGL_DEBUG("dbus message get (%s)", val); + + pid_t rid = std::stol(std::string(val)); + AGL_DEBUG("RID = %d", rid); + + g_object_unref(conn); + g_object_unref(msg); + g_object_unref(re); + + return rid; +} + +volatile sig_atomic_t e_flag = 0; + +static void sigterm_handler (int signum) +{ + e_flag = 1; +} + +static void init_signal (void) +{ + struct sigaction act, info; + + /* Setup signal for SIGTERM */ + if (!sigaction(SIGTERM, NULL, &info)) { + if (info.sa_handler == SIG_IGN) { + AGL_DEBUG("SIGTERM being ignored."); + } else if (info.sa_handler == SIG_DFL) { + AGL_DEBUG("SIGTERM being defaulted."); + } + } + + act.sa_handler = &sigterm_handler; + if (sigemptyset(&act.sa_mask) != 0) { + AGL_FATAL("Cannot initialize sigaction"); + } + act.sa_flags = 0; + + if (sigaction(SIGTERM, &act, &info) != 0) { + AGL_FATAL("Cannot register signal handler for SIGTERM"); + } +} + +int RunXDG::init_wm (void) +{ + m_wm = new LibWindowmanager(); + if (m_wm->init(m_port, m_token.c_str())) { + AGL_DEBUG("cannot initialize windowmanager"); + return -1; + } + + std::function< void(json_object*) > h_active = [](json_object* object) { + AGL_DEBUG("Got Event_Active"); + }; + + std::function< void(json_object*) > h_inactive = [](json_object* object) { + AGL_DEBUG("Got Event_Inactive"); + }; + + std::function< void(json_object*) > h_visible = [](json_object* object) { + AGL_DEBUG("Got Event_Visible"); + }; + + std::function< void(json_object*) > h_invisible = [](json_object* object) { + AGL_DEBUG("Got Event_Invisible"); + }; + + std::function< void(json_object*) > h_syncdraw = + [this](json_object* object) { + AGL_DEBUG("Got Event_SyncDraw"); + json_object* obj = json_object_new_object(); + json_object_object_add(obj, this->m_wm->kKeyDrawingName, + json_object_new_string(this->m_role.c_str())); + this->m_wm->endDraw(obj); + }; + + std::function< void(json_object*) > h_flushdraw= [](json_object* object) { + AGL_DEBUG("Got Event_FlushDraw"); + }; + + m_wm->set_event_handler(LibWindowmanager::Event_Active, h_active); + m_wm->set_event_handler(LibWindowmanager::Event_Inactive, h_inactive); + m_wm->set_event_handler(LibWindowmanager::Event_Visible, h_visible); + m_wm->set_event_handler(LibWindowmanager::Event_Invisible, h_invisible); + m_wm->set_event_handler(LibWindowmanager::Event_SyncDraw, h_syncdraw); + m_wm->set_event_handler(LibWindowmanager::Event_FlushDraw, h_flushdraw); + + return 0; +} + +int RunXDG::init_hs (void) +{ + m_hs = new LibHomeScreen(); + if (m_hs->init(m_port, m_token.c_str())) { + AGL_DEBUG("cannot initialize homescreen"); + return -1; + } + + std::function< void(json_object*) > handler = [this] (json_object* object) { + json_object *val; + + if (json_object_object_get_ex(object, "application_name", &val)) { + const char *name = json_object_get_string(val); + + AGL_DEBUG("Event_TapShortcut <%s>", name); + + if (strcmp(name, this->m_role.c_str()) == 0) { + // check app exist and re-launch if needed + AGL_DEBUG("Activesurface %s ", this->m_role.c_str()); + + json_object *obj = json_object_new_object(); + json_object_object_add(obj, this->m_wm->kKeyDrawingName, + json_object_new_string(this->m_role.c_str())); + json_object_object_add(obj, this->m_wm->kKeyDrawingArea, + json_object_new_string("normal.full")); + + this->m_wm->activateSurface(obj); + } + } + }; + m_hs->set_event_handler(LibHomeScreen::Event_TapShortcut, handler); + + std::function< void(json_object*) > h_default= [](json_object* object) { + const char *j_str = json_object_to_json_string(object); + AGL_DEBUG("Got event [%s]", j_str); + }; + m_hs->set_event_handler(LibHomeScreen::Event_OnScreenMessage, h_default); + + return 0; +} + +int RunXDG::parse_config (const char *path_to_config) +{ + auto config = cpptoml::parse_file(path_to_config); + + if (config == nullptr) { + AGL_DEBUG("cannot parse %s", path_to_config); + return -1; + } + + AGL_DEBUG("[%s] parsed", path_to_config); + + auto app = config->get_table("application"); + if (app == nullptr) { + AGL_DEBUG("cannto find [application]"); + return -1; + } + + m_role = *(app->get_as<std::string>("role")); + m_path = *(app->get_as<std::string>("path")); + if (m_role.empty() || m_path.empty()) { + AGL_FATAL("No name or path defined in config"); + } + + std::string method = *(app->get_as<std::string>("method")); + if (method.empty()) { + method = std::string("POSIX"); + } + + POSIXLauncher *pl; + + /* Setup API of launcher */ + if (method == "POSIX") { + pl = new POSIXLauncher(); + m_launcher = pl; + } else if (method == "AFM_DBUS") { + m_launcher = new AFMDBusLauncher(); + return 0; + } else if (method == "AFM_WEBSOCKET") { + m_launcher = new AFMWebSocketLauncher(); + return 0; + } else { + AGL_FATAL("Unknown type of launcher"); + } + + // setup argv[0] + pl->m_args_v.push_back(m_path); + + // setup argv[1..n] + auto params = app->get_array_of<std::string>("params"); + for (const auto& param : *params) + { + pl->m_args_v.push_back(param); + AGL_DEBUG("params[%s]", param.c_str()); + } + + return 0; +} + +RunXDG::RunXDG (int port, const char* token, const char* id) +{ + m_id = std::string(id); + m_port = port; + m_token = std::string(token); + + + auto path = std::string(getenv("AFM_APP_INSTALL_DIR")); + path = path + "/" + RUNXDG_CONFIG; + + // Parse config file of runxdg + if (parse_config(path.c_str())) { + AGL_FATAL("Error in config"); + } + + AGL_DEBUG("id=[%s], name=[%s], path=[%s], port=%lu, token=[%s]", + m_id.c_str(), m_role.c_str(), m_path.c_str(), + m_port, m_token.c_str()); + + // Setup HomeScreen/WindowManager API + if (init_wm()) + AGL_FATAL("cannot setup wm API"); + + if (init_hs()) + AGL_FATAL("cannot setup hs API"); + + // Setup ilmController API + m_ic = new ILMControl(notify_ivi_control_cb_static, this); + + AGL_DEBUG("RunXDG created."); +} + +void RunXDG::setup_surface (int id) +{ + std::string sid = std::to_string(id); + + // This surface is mine, register pair app_name and ivi id. + json_object *obj = json_object_new_object(); + json_object_object_add(obj, m_wm->kKeyDrawingName, + json_object_new_string(m_role.c_str())); + json_object_object_add(obj, m_wm->kKeyIviId, + json_object_new_string(sid.c_str())); + + AGL_DEBUG("requestSurfaceXDG(%s,%s)", m_role.c_str(), sid.c_str()); + m_wm->requestSurfaceXDG(obj); + + if (m_pending_create) { + // Recovering 1st time tap_shortcut is dropped because + // the application has not been run yet (1st time launch) + m_pending_create = false; + + json_object *obj = json_object_new_object(); + json_object_object_add(obj, m_wm->kKeyDrawingName, + json_object_new_string(m_role.c_str())); + json_object_object_add(obj, m_wm->kKeyDrawingArea, + json_object_new_string("normal.full")); + m_wm->activateSurface(obj); + } +} + +void POSIXLauncher::register_surfpid (pid_t surf_pid) +{ + if (surf_pid == m_rid) { + if (!std::count(m_pid_v.begin(), m_pid_v.end(), surf_pid)) { + AGL_DEBUG("surface creator(pid=%d) registered", surf_pid); + m_pid_v.push_back(surf_pid); + AGL_DEBUG("m_pid_v.count(%d) = %d", surf_pid, + std::count(m_pid_v.begin(), m_pid_v.end(), surf_pid)); + } + } +} + +void POSIXLauncher::unregister_surfpid (pid_t surf_pid) +{ + auto itr = m_pid_v.begin(); + while (itr != m_pid_v.end()) { + if (*itr == surf_pid) { + m_pid_v.erase(itr++); + } else { + ++itr; + } + } +} + +pid_t POSIXLauncher::find_surfpid_by_rid (pid_t rid) +{ + AGL_DEBUG("find surfpid by rid(%d)", rid); + if (std::count(m_pid_v.begin(), m_pid_v.end(), rid)) { + AGL_DEBUG("found return(%d)", rid); + return rid; + } + + return -1; +} + +void AFMLauncher::register_surfpid (pid_t surf_pid) +{ + pid_t pgid = 0; + + pgid = getpgid(surf_pid); + + if (pgid < 0) { + AGL_DEBUG("fail to get process group id"); + return; + } + + AGL_DEBUG("Surface creator is pid=%d, pgid=%d", surf_pid, pgid); + + if (!m_pgids.count(pgid)) { + m_pgids[pgid] = surf_pid; + } +} + +void AFMLauncher::unregister_surfpid (pid_t surf_pid) +{ + auto itr = m_pgids.begin(); + while (itr != m_pgids.end()) { + if (itr->second == surf_pid) { + m_pgids.erase(itr++); + } else { + ++itr; + } + } +} + +pid_t AFMLauncher::find_surfpid_by_rid (pid_t rid) +{ + auto itr = m_pgids.find(rid); + if (itr != m_pgids.end()) + return itr->second; + + return -1; +} + +void RunXDG::start (void) +{ + // Initialize SIGTERM handler + init_signal(); + + /* Launch XDG application */ + m_launcher->m_rid = m_launcher->launch(m_id); + if (m_launcher->m_rid < 0) { + AGL_FATAL("cannot launch XDG app (%s)", m_id); + } + + // take care 1st time launch + AGL_DEBUG("waiting for notification: surafce created"); + m_pending_create = true; + + // in case, target app has already run + if (m_launcher->m_rid) { + pid_t surf_pid = m_launcher->find_surfpid_by_rid(m_launcher->m_rid); + if (surf_pid > 0) { + AGL_DEBUG("match: surf:pid=%d, afm:rid=%d", surf_pid, + m_launcher->m_rid); + auto itr = m_surfaces.find(surf_pid); + if (itr != m_surfaces.end()) { + int id = itr->second; + AGL_DEBUG("surface %d for <%s> already exists", id, + m_role.c_str()); + setup_surface(id); + } + } + } + m_launcher->loop(e_flag); +} + +int main (int argc, const char* argv[]) +{ + // Set debug flags + // setenv("USE_HMI_DEBUG", "5", 1); + // setenv("WAYLAND_DEBUG", "1", 1); + + // Parse args + int port; + const char *token; + + if (argc < 3) { + AGL_FATAL("Missing port and token"); + } + + // Get app id + const char *afm_id = getenv("AFM_ID"); + if (afm_id == NULL || !afm_id[0]) { + afm_id = argv[0]; + } + + try { + port = std::stol(argv[1]); + token = argv[2]; + } catch (const std::invalid_argument& e) { + AGL_FATAL("Invalid argument"); + } catch (const std::out_of_range& e) { + AGL_FATAL("Out of range"); + } + + RunXDG runxdg(port, token, afm_id); + + runxdg.start(); + + return 0; +} diff --git a/src/runxdg.hpp b/src/runxdg.hpp new file mode 100644 index 0000000..ce0c016 --- /dev/null +++ b/src/runxdg.hpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2017 Panasonic Corporation + * + * 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. + */ +#ifndef RUNXDG_HPP +#define RUNXDG_HPP + +#include <string> +#include <vector> +#include <map> +#include <algorithm> + +#include <gio/gio.h> + +#include <ilm/ilm_control.h> + +#include <libwindowmanager.h> +#include <libhomescreen.hpp> + +#define AGL_FATAL(fmt, ...) fatal("ERROR: " fmt "\n", ##__VA_ARGS__) +#define AGL_WARN(fmt, ...) warn("WARNING: " fmt "\n", ##__VA_ARGS__) +#define AGL_DEBUG(fmt, ...) debug("DEBUG: " fmt "\n", ##__VA_ARGS__) +#define AGL_TRACE(file,line) debug("%s:%d\n", file,line); + +void fatal (const char* format, ...); +void warn (const char* format, ...); +void debug (const char* format, ...); + +class ILMControl +{ + public: + ILMControl(notificationFunc callback, void *user_data) { + ilm_init(); + ilm_registerNotification(callback, user_data); + } + + ~ILMControl(void) { + ilm_unregisterNotification(); + ilm_destroy(); + AGL_DEBUG("ilm_destory().\n"); + } +}; + +class Launcher +{ + public: + virtual void register_surfpid(pid_t surf_pid) = 0; + virtual void unregister_surfpid(pid_t surf_pid) = 0; + virtual pid_t find_surfpid_by_rid(pid_t app_pid) = 0; + + virtual int launch(std::string& name) = 0; + virtual void loop(volatile sig_atomic_t& e_flag) = 0; + + int m_rid = 0; +}; + +class POSIXLauncher : public Launcher +{ + private: + std::vector<pid_t> m_pid_v; + + public: + std::vector<std::string> m_args_v; + + void register_surfpid(pid_t surf_pid); + void unregister_surfpid(pid_t surf_pid); + pid_t find_surfpid_by_rid(pid_t rid); + + int launch(std::string& name); + void loop(volatile sig_atomic_t& e_flag); +}; + +class AFMLauncher : public Launcher +{ + private: + std::map<int, int> m_pgids; // pair of <afm:rid, ivi:pid> + + public: + void register_surfpid(pid_t surf_pid); + void unregister_surfpid(pid_t surf_pid); + pid_t find_surfpid_by_rid(pid_t app_pid); +}; + +class AFMDBusLauncher : public AFMLauncher +{ + public: + int launch(std::string& name); + void loop(volatile sig_atomic_t& e_flag) { + while (!(e_flag)) { sleep(60*60*24); } } + + private: + int get_dbus_message_bus(GBusType bus_type, GDBusConnection* &conn); + + const char* DBUS_SERVICE = "org.AGL.afm.user"; + const char* DBUS_PATH = "/org/AGL/afm/user"; + const char* DBUS_INTERFACE = "org.AGL.afm.user"; +}; + +class AFMWebSocketLauncher : public AFMLauncher +{ + // not implemented yet + public: + int launch(std::string& name) { return 0; } + void loop(volatile sig_atomic_t& e_flag) { + while (!(e_flag)) { sleep(60*60*24); } } +}; + +class RunXDG +{ + public: + RunXDG(int port, const char* token, const char* id); + + void start(void); + void notify_ivi_control_cb(ilmObjectType object, t_ilm_uint id, + t_ilm_bool created); + static void notify_ivi_control_cb_static (ilmObjectType object, + t_ilm_uint id, + t_ilm_bool created, + void *user_data); + private: + std::string m_role; + std::string m_path; + + std::string m_id; + + int m_port; + std::string m_token; + + Launcher *m_launcher; + + LibWindowmanager *m_wm; + LibHomeScreen *m_hs; + ILMControl *m_ic; + + std::map<int, int> m_surfaces; // pair of <afm:rid, ivi:id> + + bool m_pending_create = false; + + int init_wm(void); + int init_hs(void); + + int parse_config(const char *file); + + void setup_surface(int id); +}; + +#endif // RUNXDG_HPP |