aboutsummaryrefslogtreecommitdiff
path: root/source/lib/system
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2023-08-17 12:32:50 +0200
committerFelix Morgner <felix.morgner@gmail.com>2023-08-17 12:32:50 +0200
commit375799fa79d1af76f33299acc20a11a167a021f8 (patch)
tree8f91b982ec96225c33a2f4871730ababffe5cab0 /source/lib/system
parentaf471b9b780869915d3217b228e24d025892de47 (diff)
downloadwanda-375799fa79d1af76f33299acc20a11a167a021f8.tar.xz
wanda-375799fa79d1af76f33299acc20a11a167a021f8.zip
project: restructure libraries and build env
Diffstat (limited to 'source/lib/system')
-rw-r--r--source/lib/system/CMakeLists.txt68
-rw-r--r--source/lib/system/include/wanda/system/environment.hpp61
-rw-r--r--source/lib/system/include/wanda/system/filesystem.hpp39
-rw-r--r--source/lib/system/include/wanda/system/logging.hpp35
-rw-r--r--source/lib/system/include/wanda/system/magic.hpp58
-rw-r--r--source/lib/system/include/wanda/system/setting.hpp156
-rw-r--r--source/lib/system/include/wanda/system/wallpaper.hpp24
-rw-r--r--source/lib/system/include/wanda/system/xdg.hpp40
-rw-r--r--source/lib/system/src/environment.cpp71
-rw-r--r--source/lib/system/src/filesystem.cpp31
-rw-r--r--source/lib/system/src/logging.cpp21
-rw-r--r--source/lib/system/src/setting.cpp102
-rw-r--r--source/lib/system/src/wallpaper.cpp91
-rw-r--r--source/lib/system/src/xdg.cpp46
14 files changed, 843 insertions, 0 deletions
diff --git a/source/lib/system/CMakeLists.txt b/source/lib/system/CMakeLists.txt
new file mode 100644
index 0000000..813bcc7
--- /dev/null
+++ b/source/lib/system/CMakeLists.txt
@@ -0,0 +1,68 @@
+cmake_path(GET CMAKE_CURRENT_SOURCE_DIR STEM LIB_NAME)
+
+file(GLOB_RECURSE LIB_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" CONFIGURE_DEPENDS "**/*.hpp")
+file(GLOB_RECURSE LIB_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" CONFIGURE_DEPENDS "**/*.cpp")
+
+find_package("Boost" REQUIRED)
+find_package("fmt" REQUIRED)
+find_package("JPEG" REQUIRED)
+find_package("PNG" REQUIRED)
+find_package("spdlog" REQUIRED)
+
+find_package("PkgConfig" REQUIRED)
+
+pkg_check_modules("GIO"
+ REQUIRED
+ IMPORTED_TARGET
+ GLOBAL
+ "gio-2.0"
+)
+
+pkg_check_modules("libmagic"
+ REQUIRED
+ IMPORTED_TARGET
+ GLOBAL
+ "libmagic"
+)
+
+add_library("wanda-${LIB_NAME}"
+ ${LIB_SOURCES}
+)
+
+target_sources("wanda-${LIB_NAME}" INTERFACE
+ FILE_SET HEADERS
+ FILES ${LIB_HEADERS}
+ BASE_DIRS "include"
+)
+
+target_include_directories("wanda-${LIB_NAME}" PUBLIC
+ "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
+)
+
+target_include_directories("wanda-${LIB_NAME}" SYSTEM PUBLIC
+ "$<INSTALL_INTERFACE:include>"
+)
+
+target_compile_features("wanda-${LIB_NAME}" PUBLIC
+ "cxx_std_20"
+)
+
+target_link_libraries("wanda-${LIB_NAME}" PUBLIC
+ "wanda::meta"
+ "wanda::std_ext"
+
+ "boost::boost"
+ "fmt::fmt"
+ "JPEG::JPEG"
+ "PNG::PNG"
+ "spdlog::spdlog"
+
+ "PkgConfig::GIO"
+ "PkgConfig::libmagic"
+)
+
+install(TARGETS "wanda-${LIB_NAME}"
+ FILE_SET HEADERS
+)
+
+add_library("wanda::${LIB_NAME}" ALIAS "wanda-${LIB_NAME}")
diff --git a/source/lib/system/include/wanda/system/environment.hpp b/source/lib/system/include/wanda/system/environment.hpp
new file mode 100644
index 0000000..8cd9ecf
--- /dev/null
+++ b/source/lib/system/include/wanda/system/environment.hpp
@@ -0,0 +1,61 @@
+/**
+ * @file environment.hpp
+ * @author Felix Morgner (felix.morgner@gmail.com)
+ * @since 1.0.0
+ */
+
+#ifndef WANDA_SYSTEM_ENVIRONMENT_HPP
+#define WANDA_SYSTEM_ENVIRONMENT_HPP
+
+#include <unistd.h>
+
+#include <map>
+#include <string>
+
+namespace wanda::system
+{
+ /**
+ * @brief A type to provide access to the runtime environment
+ */
+ struct environment
+ {
+ using map_type = std::map<std::string, std::string>;
+ using iterator = map_type::iterator;
+ using const_iterator = map_type::const_iterator;
+ using reference = map_type::reference;
+ using const_reference = map_type::const_reference;
+
+ /**
+ * @brief Construct a new environment from the given string array
+ */
+ explicit environment(char const * const * env = ::environ);
+
+ /**
+ * @brief Get the value of the given variable
+ *
+ * @return A mutable reference to the value of the given environment variable
+ */
+ std::string & operator[](std::string const & variable);
+
+ /**
+ * @brief Get the value of the given variable
+ *
+ * @return An immutable reference to the value of the given environment variable
+ */
+ std::string const & operator[](std::string const & variable) const;
+
+ iterator begin();
+ const_iterator begin() const;
+ const_iterator cbegin() const;
+
+ iterator end();
+ const_iterator end() const;
+ const_iterator cend() const;
+
+ private:
+ map_type m_cache{};
+ };
+
+} // namespace wanda::system
+
+#endif \ No newline at end of file
diff --git a/source/lib/system/include/wanda/system/filesystem.hpp b/source/lib/system/include/wanda/system/filesystem.hpp
new file mode 100644
index 0000000..971db90
--- /dev/null
+++ b/source/lib/system/include/wanda/system/filesystem.hpp
@@ -0,0 +1,39 @@
+/**
+ * @file filesystem.hpp
+ * @author Felix Morgner (felix.morgner@gmail.com)
+ * @since 1.0.0
+ */
+
+#ifndef WANDA_SYSTEM_FILESYSTEM_HPP
+#define WANDA_SYSTEM_FILESYSTEM_HPP
+
+#include <filesystem>
+#include <optional>
+#include <vector>
+
+namespace wanda::system
+{
+ /**
+ * @brief Covenience alias for path lists
+ */
+ using path_list = std::vector<std::filesystem::path>;
+
+ /**
+ * @brief The default scan filter, allowing only regular files to pass
+ */
+ constexpr inline auto default_filter = [](std::filesystem::path const & path) {
+ return is_regular_file(path);
+ };
+
+ /**
+ * @brief Scan the given folder for files
+ */
+ std::optional<path_list> scan(std::filesystem::path folder, bool(filter)(std::filesystem::path const &) = default_filter);
+
+ /**
+ * @brief Pick a random path from the given list
+ */
+ std::filesystem::path random_pick(path_list const & paths);
+} // namespace wanda::system
+
+#endif \ No newline at end of file
diff --git a/source/lib/system/include/wanda/system/logging.hpp b/source/lib/system/include/wanda/system/logging.hpp
new file mode 100644
index 0000000..8a9a90e
--- /dev/null
+++ b/source/lib/system/include/wanda/system/logging.hpp
@@ -0,0 +1,35 @@
+/**
+ * @file logging.hpp
+ * @author Felix Morgner (felix.morgner@gmail.com)
+ * @since 1.0.0
+ */
+
+#ifndef WANDA_SYSTEM_LOGGING_HPP
+#define WANDA_SYSTEM_LOGGING_HPP
+
+#include <spdlog/sinks/null_sink.h>
+#include <spdlog/spdlog.h>
+
+#include <memory>
+
+namespace wanda::system
+{
+ /**
+ * @brief A covenience alias to represent a handle for a logger
+ */
+ using logger_ptr = std::shared_ptr<spdlog::logger>;
+
+ /**
+ * @brief Initialize the shared logger
+ *
+ * @note The logger will only ever be initialized once, even if this function is called multiple times
+ */
+ void initialize_logger(spdlog::sink_ptr sink = std::make_shared<spdlog::sinks::null_sink_st>());
+
+ /**
+ * @brief Get the shared logger
+ */
+ logger_ptr get_logger();
+} // namespace wanda::system
+
+#endif \ No newline at end of file
diff --git a/source/lib/system/include/wanda/system/magic.hpp b/source/lib/system/include/wanda/system/magic.hpp
new file mode 100644
index 0000000..a8ea1ba
--- /dev/null
+++ b/source/lib/system/include/wanda/system/magic.hpp
@@ -0,0 +1,58 @@
+#ifndef WANDA_SYSTEM_MAGIC_HPP
+#define WANDA_SYSTEM_MAGIC_HPP
+
+#include <magic.h>
+
+#include <filesystem>
+#include <memory>
+#include <string>
+#include <type_traits>
+
+namespace wanda::system
+{
+ struct magic
+ {
+ struct closer
+ {
+ auto operator()(magic_t handle) const noexcept -> void
+ {
+ magic_close(handle);
+ }
+ };
+
+ enum struct mime_type
+ {
+ unknown,
+ image_jpeg,
+ image_png,
+ };
+
+ magic()
+ : m_handle{magic_open(MAGIC_MIME_TYPE)}
+ {
+ magic_load(m_handle.get(), nullptr);
+ }
+
+ auto type(std::filesystem::path path) -> mime_type
+ {
+ auto magic_type = std::string{magic_file(m_handle.get(), path.native().c_str())};
+
+ if (magic_type == "image/jpeg")
+ {
+ return mime_type::image_jpeg;
+ }
+ else if (magic_type == "image/png")
+ {
+ return mime_type::image_png;
+ }
+
+ return mime_type::unknown;
+ }
+
+ private:
+ std::unique_ptr<std::remove_pointer_t<magic_t>, closer> m_handle;
+ };
+
+} // namespace wanda::system
+
+#endif \ No newline at end of file
diff --git a/source/lib/system/include/wanda/system/setting.hpp b/source/lib/system/include/wanda/system/setting.hpp
new file mode 100644
index 0000000..e0be3f4
--- /dev/null
+++ b/source/lib/system/include/wanda/system/setting.hpp
@@ -0,0 +1,156 @@
+/**
+ * @file setting.hpp
+ * @author Felix Morgner (felix.morgner@gmail.com)
+ * @since 1.0.0
+ */
+
+#ifndef WANDA_SYSTEM_SETTING_HPP
+#define WANDA_SYSTEM_SETTING_HPP
+
+#include "wanda/meta/deferred_failure.hpp"
+#include "wanda/meta/type_wrapper.hpp"
+
+#include <gio/gio.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <variant>
+#include <vector>
+
+namespace wanda::system
+{
+ struct setting;
+
+ /**
+ * @brief A convenience type to represent setting keys
+ */
+ using key = meta::type_wrapper<std::string, struct KeyTag>;
+
+ namespace literals
+ {
+ /**
+ * @brief UDL to create setting keys
+ */
+ key operator""_key(char const * str, std::size_t len);
+
+ /**
+ * @brief UDL to create setting schemas
+ */
+ std::optional<setting> operator""_setting(char const * str, std::size_t lent);
+ } // namespace literals
+
+ /**
+ * @brief A simple wrapper for GSettings Schemas
+ */
+ struct setting
+ {
+ struct entry
+ {
+ using value_type = std::variant<std::monostate, bool, std::int32_t, std::int64_t, std::uint32_t, std::uint64_t, double, std::string, std::vector<std::string>>;
+
+ /**
+ * @brief Get the value of the settings entry
+ */
+ value_type operator*() const;
+
+ /**
+ * @brief Assign the given @p value to the settings entry
+ *
+ * @returns @p true iff. the value could be successfully assigned
+ */
+ template<typename Type>
+ bool operator=(Type value)
+ {
+ struct setting_applier
+ {
+ setting_applier(GSettings * setting, gchar const * key, Type value) noexcept
+ : m_result{[&] {
+ if constexpr (std::is_same_v<Type, bool>)
+ {
+ return g_settings_set_boolean(setting, key, value);
+ }
+ else if constexpr (std::is_same_v<Type, std::int32_t>)
+ {
+ return g_settings_set_int(setting, key, value);
+ }
+ else if constexpr (std::is_same_v<Type, std::int64_t>)
+ {
+ return g_settings_set_int64(setting, key, value);
+ }
+ else if constexpr (std::is_same_v<Type, std::uint32_t>)
+ {
+ return g_settings_set_uint(setting, key, value);
+ }
+ else if constexpr (std::is_same_v<Type, std::uint64_t>)
+ {
+ return g_settings_set_uint64(setting, key, value);
+ }
+ else if constexpr (std::is_same_v<Type, double>)
+ {
+ return g_settings_set_double(setting, key, value);
+ }
+ else if constexpr (std::is_same_v<Type, std::string>)
+ {
+ return g_settings_set_string(setting, key, value.c_str());
+ }
+ else if constexpr (std::is_same_v<Type, std::vector<std::string>>)
+ {
+ auto temp = std::vector<gchar const *>{value.size() + 1};
+ std::transform(value.begin(), value.end(), temp.begin(), [](auto const & str) { return str.c_str(); });
+ return g_settings_set_strv(setting, key, temp.data());
+ }
+ }()}
+ {
+ }
+
+ ~setting_applier()
+ {
+ g_settings_sync();
+ }
+
+ operator bool() const
+ {
+ return m_result;
+ }
+
+ private:
+ gboolean const m_result;
+ };
+
+ return setting_applier{m_settings.get(), m_key.get().c_str(), value};
+ }
+
+ private:
+ entry(setting const & schema, key key);
+
+ std::unique_ptr<GSettings, decltype(&g_object_unref)> m_settings;
+
+ key m_key;
+
+ friend setting;
+ };
+
+ /**
+ * @brief Get the entry for the given key
+ *
+ * @return An <code>std::optional</code> wrapping the entry associated with
+ * the given key, or an empty <code>std::optional</code> if the desired key
+ * does not exist in the setting's schema.
+ */
+ std::optional<entry> operator[](key key) const;
+
+ private:
+ explicit setting(GSettingsSchema * schema);
+
+ std::unique_ptr<GSettingsSchema, decltype(&g_settings_schema_unref)> m_schema;
+
+ friend std::optional<setting> literals::operator""_setting(char const *, std::size_t);
+ };
+
+} // namespace wanda::system
+
+#endif \ No newline at end of file
diff --git a/source/lib/system/include/wanda/system/wallpaper.hpp b/source/lib/system/include/wanda/system/wallpaper.hpp
new file mode 100644
index 0000000..7965fb0
--- /dev/null
+++ b/source/lib/system/include/wanda/system/wallpaper.hpp
@@ -0,0 +1,24 @@
+/**
+ * @file wallpaper.hpp
+ * @author Felix Morgner (felix.morgner@gmail.com)
+ * @since 1.0.0
+ */
+
+#ifndef WANDA_SYSTEM_WALLPAPER_HPP
+#define WANDA_SYSTEM_WALLPAPER_HPP
+
+#include <spdlog/sinks/null_sink.h>
+#include <spdlog/spdlog.h>
+
+#include <filesystem>
+#include <memory>
+
+namespace wanda::system
+{
+ /**
+ * @brief Set the wallpaper to the file specified by the given path
+ */
+ void set_wallpaper(std::filesystem::path wallpaper);
+} // namespace wanda::system
+
+#endif \ No newline at end of file
diff --git a/source/lib/system/include/wanda/system/xdg.hpp b/source/lib/system/include/wanda/system/xdg.hpp
new file mode 100644
index 0000000..ae01feb
--- /dev/null
+++ b/source/lib/system/include/wanda/system/xdg.hpp
@@ -0,0 +1,40 @@
+/**
+ * @file xdg.hpp
+ * @author Felix Morgner (felix.morgner@gmail.com)
+ * @since 1.0.0
+ */
+
+#ifndef WANDA_SYSTEM_XDG_HPP
+#define WANDA_SYSTEM_XDG_HPP
+
+#include "wanda/system/environment.hpp"
+
+#include <cstddef>
+#include <filesystem>
+#include <type_traits>
+
+namespace wanda::system
+{
+ /**
+ * @brief An @p enum to represet the standardized XDG directories
+ */
+ enum struct xdg_directory : std::underlying_type_t<std::byte>
+ {
+ data_home,
+ config_home,
+ cache_home,
+ runtime_dir,
+ };
+
+ /**
+ * @brief Get the name of the environment variable associated with the given XDG directory
+ */
+ std::string xdg_variable(xdg_directory directory);
+
+ /**
+ * @brief Get the path to the given @p directory given the provided @p environment
+ */
+ std::filesystem::path xdg_path_for(xdg_directory directory, environment const & environment);
+} // namespace wanda::system
+
+#endif \ No newline at end of file
diff --git a/source/lib/system/src/environment.cpp b/source/lib/system/src/environment.cpp
new file mode 100644
index 0000000..8f79531
--- /dev/null
+++ b/source/lib/system/src/environment.cpp
@@ -0,0 +1,71 @@
+#include "wanda/system/environment.hpp"
+
+#include <string>
+
+namespace wanda::system
+{
+ environment::environment(char const * const * env)
+ {
+ if (!env)
+ {
+ return;
+ }
+
+ std::string buffer{};
+ for (; *env != nullptr; ++env)
+ {
+ buffer = *env;
+ int split_point = buffer.find('=');
+ if (split_point != std::string::npos)
+ {
+ m_cache[buffer.substr(0, split_point)] = buffer.substr(split_point + 1);
+ }
+ }
+ }
+
+ std::string & environment::operator[](std::string const & variable)
+ {
+ return m_cache[variable];
+ }
+
+ std::string const & environment::operator[](std::string const & variable) const
+ {
+ static std::string const empty{};
+ if (auto needle = m_cache.find(variable); needle != cend())
+ {
+ return needle->second;
+ }
+ return empty;
+ }
+
+ environment::iterator environment::begin()
+ {
+ return m_cache.begin();
+ }
+
+ environment::const_iterator environment::begin() const
+ {
+ return m_cache.begin();
+ }
+
+ environment::const_iterator environment::cbegin() const
+ {
+ return m_cache.cbegin();
+ }
+
+ environment::iterator environment::end()
+ {
+ return m_cache.end();
+ }
+
+ environment::const_iterator environment::end() const
+ {
+ return m_cache.end();
+ }
+
+ environment::const_iterator environment::cend() const
+ {
+ return m_cache.cend();
+ }
+
+} // namespace wanda::system \ No newline at end of file
diff --git a/source/lib/system/src/filesystem.cpp b/source/lib/system/src/filesystem.cpp
new file mode 100644
index 0000000..04dfff3
--- /dev/null
+++ b/source/lib/system/src/filesystem.cpp
@@ -0,0 +1,31 @@
+#include "wanda/system/filesystem.hpp"
+
+#include <random>
+#include <ranges>
+
+namespace wanda::system
+{
+ std::optional<path_list> scan(std::filesystem::path source, bool(filter)(std::filesystem::path const &))
+ {
+ if (!std::filesystem::is_directory(source))
+ {
+ return std::nullopt;
+ }
+ auto entries = std::filesystem::recursive_directory_iterator{source};
+ auto result = path_list{};
+ for (auto & entry : entries | std::views::filter(filter))
+ {
+ result.push_back(entry.path());
+ }
+ return result;
+ }
+
+ std::filesystem::path random_pick(path_list const & paths)
+ {
+ static auto generator = std::mt19937{std::random_device{}()};
+ auto distribution = std::uniform_int_distribution<std::size_t>{0, paths.size() - 1};
+
+ return paths[distribution(generator)];
+ }
+
+} // namespace wanda::system \ No newline at end of file
diff --git a/source/lib/system/src/logging.cpp b/source/lib/system/src/logging.cpp
new file mode 100644
index 0000000..83dc9f6
--- /dev/null
+++ b/source/lib/system/src/logging.cpp
@@ -0,0 +1,21 @@
+#include "wanda/system/logging.hpp"
+
+namespace wanda::system
+{
+ std::function<void(spdlog::sink_ptr)> initializer = [](spdlog::sink_ptr sink) {
+ spdlog::register_logger(std::make_shared<spdlog::logger>("wanda", sink));
+ initializer = [](auto) {};
+ };
+
+ void initialize_logger(spdlog::sink_ptr sink)
+ {
+ initializer(sink);
+ }
+
+ logger_ptr get_logger()
+ {
+ initialize_logger();
+ return spdlog::get("wanda");
+ }
+
+} // namespace wanda::system \ No newline at end of file
diff --git a/source/lib/system/src/setting.cpp b/source/lib/system/src/setting.cpp
new file mode 100644
index 0000000..1553eae
--- /dev/null
+++ b/source/lib/system/src/setting.cpp
@@ -0,0 +1,102 @@
+#include "wanda/system/setting.hpp"
+
+#include <algorithm>
+#include <type_traits>
+
+namespace wanda::system
+{
+ // UDL implementations
+
+ key literals::operator""_key(char const * str, std::size_t len)
+ {
+ return key{{str, len}};
+ }
+
+ std::optional<setting> literals::operator""_setting(char const * str, std::size_t len)
+ {
+ auto source = g_settings_schema_source_get_default();
+ if (!source)
+ {
+ return std::nullopt;
+ }
+
+ auto schema = g_settings_schema_source_lookup(source, str, true);
+ if (!schema)
+ {
+ return std::nullopt;
+ }
+
+ return setting{schema};
+ }
+
+ // 'setting' implementation
+
+ setting::setting(GSettingsSchema * schema)
+ : m_schema{schema, &g_settings_schema_unref}
+ {
+ }
+
+ std::optional<setting::entry> setting::operator[](key key) const
+ {
+ if (!g_settings_schema_has_key(m_schema.get(), key.get().c_str()))
+ {
+ return std::nullopt;
+ }
+
+ return setting::entry{*this, std::move(key)};
+ }
+
+ // 'setting::entry' implementation
+
+ setting::entry::entry(setting const & setting, key key)
+ : m_settings{g_settings_new(g_settings_schema_get_id(setting.m_schema.get())), &g_object_unref}
+ , m_key{key.get()}
+ {
+ }
+
+ setting::entry::value_type setting::entry::operator*() const
+ {
+ auto value = std::unique_ptr<GVariant, decltype(&g_variant_unref)>{g_settings_get_value(m_settings.get(), m_key.get().c_str()), &g_variant_unref};
+ auto raw = value.get();
+
+ if (g_variant_is_of_type(raw, G_VARIANT_TYPE_BOOLEAN))
+ {
+ return static_cast<bool>(g_variant_get_boolean(raw));
+ }
+ else if (g_variant_is_of_type(raw, G_VARIANT_TYPE_INT32))
+ {
+ return static_cast<std::int32_t>(g_variant_get_int32(raw));
+ }
+ else if (g_variant_is_of_type(raw, G_VARIANT_TYPE_INT64))
+ {
+ return static_cast<std::int64_t>(g_variant_get_int64(raw));
+ }
+ else if (g_variant_is_of_type(raw, G_VARIANT_TYPE_UINT32))
+ {
+ return static_cast<std::uint32_t>(g_variant_get_uint32(raw));
+ }
+ else if (g_variant_is_of_type(raw, G_VARIANT_TYPE_UINT64))
+ {
+ return static_cast<std::uint64_t>(g_variant_get_uint64(raw));
+ }
+ else if (g_variant_is_of_type(raw, G_VARIANT_TYPE_DOUBLE))
+ {
+ return static_cast<double>(g_variant_get_double(raw));
+ }
+ else if (g_variant_is_of_type(raw, G_VARIANT_TYPE_STRING))
+ {
+ auto size = gsize{};
+ auto string = g_variant_get_string(raw, &size);
+ return std::string{string, size};
+ }
+ else if (g_variant_is_of_type(raw, G_VARIANT_TYPE_STRING_ARRAY))
+ {
+ auto length = gsize{};
+ auto data = g_variant_get_strv(raw, &length);
+ return std::vector<std::string>{data, data + length};
+ }
+
+ return {};
+ }
+
+} // namespace wanda::system \ No newline at end of file
diff --git a/source/lib/system/src/wallpaper.cpp b/source/lib/system/src/wallpaper.cpp
new file mode 100644
index 0000000..ebb8bd7
--- /dev/null
+++ b/source/lib/system/src/wallpaper.cpp
@@ -0,0 +1,91 @@
+#include "wanda/system/wallpaper.hpp"
+
+#include "wanda/std_ext/optional.hpp"
+#include "wanda/system/logging.hpp"
+#include "wanda/system/magic.hpp"
+#include "wanda/system/setting.hpp"
+
+#include <boost/gil.hpp>
+#include <boost/gil/extension/io/jpeg.hpp>
+#include <boost/gil/extension/io/png.hpp>
+#include <boost/gil/extension/numeric/resample.hpp>
+#include <boost/gil/extension/numeric/sampler.hpp>
+#include <fmt/format.h>
+
+#include <algorithm>
+#include <cmath>
+#include <memory>
+
+namespace wanda::system
+{
+ namespace
+ {
+ auto magic_instance = magic{};
+
+ auto load_image(std::filesystem::path wallpaper)
+ {
+ auto image = boost::gil::rgb8_image_t{};
+
+ switch (magic_instance.type(wallpaper))
+ {
+ case magic::mime_type::image_jpeg:
+ boost::gil::read_and_convert_image(wallpaper.native(), image, boost::gil::jpeg_tag{});
+ break;
+ case magic::mime_type::image_png:
+ boost::gil::read_and_convert_image(wallpaper.native(), image, boost::gil::png_tag{});
+ break;
+ }
+
+ return image;
+ }
+
+ auto average_colors(boost::gil::rgb8_image_t image)
+ {
+ auto accumulator = boost::gil::rgb64f_pixel_t{};
+ auto view = const_view(image);
+
+ std::ranges::for_each(view, [&](auto const & source_pixel) {
+ at_c<0>(accumulator) += std::pow(boost::gil::at_c<0>(source_pixel), 2);
+ at_c<1>(accumulator) += std::pow(boost::gil::at_c<1>(source_pixel), 2);
+ at_c<2>(accumulator) += std::pow(boost::gil::at_c<2>(source_pixel), 2);
+ });
+
+ at_c<0>(accumulator) = std::sqrt(at_c<0>(accumulator) / view.size());
+ at_c<1>(accumulator) = std::sqrt(at_c<1>(accumulator) / view.size());
+ at_c<2>(accumulator) = std::sqrt(at_c<2>(accumulator) / view.size());
+
+ return accumulator;
+ }
+
+ } // namespace
+
+ void set_wallpaper(std::filesystem::path wallpaper)
+ {
+ using namespace wanda::system::literals;
+ using namespace wanda::std_ext;
+ using namespace std::string_literals;
+
+ auto image = load_image(wallpaper);
+ auto color = average_colors(std::move(image));
+ auto hexstring = fmt::format("#{:02X}{:02X}{:02X}",
+ static_cast<std::uint8_t>(at_c<0>(color)),
+ static_cast<std::uint8_t>(at_c<1>(color)),
+ static_cast<std::uint8_t>(at_c<2>(color)));
+
+ with("org.gnome.desktop.background"_setting, [&](auto & setting) {
+ with(setting["primary-color"_key], [&](auto & value) {
+ value = hexstring;
+ });
+ with(setting["picture-uri"_key], [&](auto & value) {
+ value = "file://" + wallpaper.native();
+ }) ||
+ [&] { get_logger()->error("invalid settings key"); };
+ with(setting["picture-uri-dark"_key], [&](auto & value) {
+ value = "file://" + wallpaper.native();
+ }) ||
+ [&] { get_logger()->error("invalid settings key"); };
+ }) ||
+ [&] { get_logger()->error("invalid setting"); };
+ }
+
+} // namespace wanda::system
diff --git a/source/lib/system/src/xdg.cpp b/source/lib/system/src/xdg.cpp
new file mode 100644
index 0000000..109beb1
--- /dev/null
+++ b/source/lib/system/src/xdg.cpp
@@ -0,0 +1,46 @@
+#include "wanda/system/xdg.hpp"
+
+#include <unistd.h>
+
+namespace wanda::system
+{
+ std::string xdg_variable(xdg_directory directory)
+ {
+ switch (directory)
+ {
+ case xdg_directory::data_home:
+ return "XDG_DATA_HOME";
+ case xdg_directory::config_home:
+ return "XDG_CONFIG_HOME";
+ case xdg_directory::cache_home:
+ return "XDG_CACHE_HOME";
+ case xdg_directory::runtime_dir:
+ return "XDG_RUNTIME_DIR";
+ }
+ return "XDG_INVALID_PATH";
+ }
+
+ std::filesystem::path xdg_path_for(xdg_directory directory, environment const & environment)
+ {
+ if (auto path = environment[xdg_variable(directory)]; !path.empty())
+ {
+ return path;
+ }
+
+ auto home = std::filesystem::path{environment["HOME"]};
+ switch (directory)
+ {
+ case xdg_directory::data_home:
+ return home / ".local/share";
+ case xdg_directory::config_home:
+ return home / ".config";
+ case xdg_directory::cache_home:
+ return home / ".cache";
+ case xdg_directory::runtime_dir:
+ return std::filesystem::path{"/run/user"} / std::to_string(::getuid());
+ }
+
+ return "";
+ }
+
+} // namespace wanda::system \ No newline at end of file