From b377520a38dd0a2aa68301039e9f649d98a94f5f Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 13 Aug 2021 06:08:27 -0700 Subject: [PATCH] refactor(client): extract config handling into a new class --- include/client.hpp | 14 +--- include/config.hpp | 31 +++++++++ meson.build | 1 + src/client.cpp | 148 +--------------------------------------- src/config.cpp | 166 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 204 insertions(+), 156 deletions(-) create mode 100644 include/config.hpp create mode 100644 src/config.cpp diff --git a/include/client.hpp b/include/client.hpp index e7fa1db0..e68e4ad0 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -3,11 +3,10 @@ #include #include #include -#include #include -#include #include "bar.hpp" +#include "config.hpp" struct zwlr_layer_shell_v1; struct zwp_idle_inhibitor_v1; @@ -32,15 +31,8 @@ class Client { private: Client() = default; - std::tuple getConfigs(const std::string &config, - const std::string &style) const; - void bindInterfaces(); - const std::string getValidPath(const std::vector &paths) const; + void bindInterfaces(); void handleOutput(struct waybar_output &output); - bool isValidOutput(const Json::Value &config, struct waybar_output &output); - auto setupConfig(const std::string &config_file, int depth) -> void; - auto resolveConfigIncludes(Json::Value &config, int depth) -> void; - auto mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void; auto setupCss(const std::string &css_file) -> void; struct waybar_output & getOutput(void *); std::vector getOutputConfigs(struct waybar_output &output); @@ -55,7 +47,7 @@ class Client { void handleMonitorRemoved(Glib::RefPtr monitor); void handleDeferredMonitorRemoval(Glib::RefPtr monitor); - Json::Value config_; + Config config_; Glib::RefPtr style_context_; Glib::RefPtr css_provider_; std::list outputs_; diff --git a/include/config.hpp b/include/config.hpp new file mode 100644 index 00000000..bb7b9068 --- /dev/null +++ b/include/config.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include + +namespace waybar { + +class Config { + public: + Config() = default; + + void load(const std::string &config, const std::string &style); + + const std::string &getStyle() { return css_file_; } + + Json::Value &getConfig() { return config_; } + + std::vector getOutputConfigs(const std::string &name, const std::string &identifier); + + private: + void setupConfig(const std::string &config_file, int depth); + void resolveConfigIncludes(Json::Value &config, int depth); + void mergeConfig(Json::Value &a_config_, Json::Value &b_config_); + + std::string config_file_; + std::string css_file_; + + Json::Value config_; +}; +} // namespace waybar diff --git a/meson.build b/meson.build index 835b70e3..641607d6 100644 --- a/meson.build +++ b/meson.build @@ -149,6 +149,7 @@ src_files = files( 'src/main.cpp', 'src/bar.cpp', 'src/client.cpp', + 'src/config.cpp', 'src/util/ustring_clen.cpp' ) diff --git a/src/client.cpp b/src/client.cpp index ff6e7bf2..b50faff7 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -3,12 +3,10 @@ #include #include -#include #include #include "idle-inhibit-unstable-v1-client-protocol.h" #include "util/clara.hpp" -#include "util/json.hpp" #include "wlr-layer-shell-unstable-v1-client-protocol.h" waybar::Client *waybar::Client::inst() { @@ -16,23 +14,6 @@ waybar::Client *waybar::Client::inst() { return c; } -const std::string waybar::Client::getValidPath(const std::vector &paths) const { - wordexp_t p; - - for (const std::string &path : paths) { - if (wordexp(path.c_str(), &p, 0) == 0) { - if (access(*p.we_wordv, F_OK) == 0) { - std::string result = *p.we_wordv; - wordfree(&p); - return result; - } - wordfree(&p); - } - } - - return std::string(); -} - void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { auto client = static_cast(data); @@ -70,29 +51,6 @@ void waybar::Client::handleOutput(struct waybar_output &output) { zxdg_output_v1_add_listener(output.xdg_output.get(), &xdgOutputListener, &output); } -bool waybar::Client::isValidOutput(const Json::Value &config, struct waybar_output &output) { - if (config["output"].isArray()) { - for (auto const &output_conf : config["output"]) { - if (output_conf.isString() && - (output_conf.asString() == output.name || output_conf.asString() == output.identifier)) { - return true; - } - } - return false; - } else if (config["output"].isString()) { - auto config_output = config["output"].asString(); - if (!config_output.empty()) { - if (config_output.substr(0, 1) == "!") { - return config_output.substr(1) != output.name && - config_output.substr(1) != output.identifier; - } - return config_output == output.name || config_output == output.identifier; - } - } - - return true; -} - struct waybar::waybar_output &waybar::Client::getOutput(void *addr) { auto it = std::find_if( outputs_.begin(), outputs_.end(), [&addr](const auto &output) { return &output == addr; }); @@ -103,17 +61,7 @@ struct waybar::waybar_output &waybar::Client::getOutput(void *addr) { } std::vector waybar::Client::getOutputConfigs(struct waybar_output &output) { - std::vector configs; - if (config_.isArray()) { - for (auto const &config : config_) { - if (config.isObject() && isValidOutput(config, output)) { - configs.push_back(config); - } - } - } else if (isValidOutput(config_, output)) { - configs.push_back(config_); - } - return configs; + return config_.getOutputConfigs(output.name, output.identifier); } void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_output*/) { @@ -203,95 +151,6 @@ void waybar::Client::handleDeferredMonitorRemoval(Glib::RefPtr mon outputs_.remove_if([&monitor](const auto &output) { return output.monitor == monitor; }); } -std::tuple waybar::Client::getConfigs( - const std::string &config, const std::string &style) const { - auto config_file = config.empty() ? getValidPath({ - "$XDG_CONFIG_HOME/waybar/config", - "$XDG_CONFIG_HOME/waybar/config.jsonc", - "$HOME/.config/waybar/config", - "$HOME/.config/waybar/config.jsonc", - "$HOME/waybar/config", - "$HOME/waybar/config.jsonc", - "/etc/xdg/waybar/config", - "/etc/xdg/waybar/config.jsonc", - SYSCONFDIR "/xdg/waybar/config", - "./resources/config", - }) - : config; - auto css_file = style.empty() ? getValidPath({ - "$XDG_CONFIG_HOME/waybar/style.css", - "$HOME/.config/waybar/style.css", - "$HOME/waybar/style.css", - "/etc/xdg/waybar/style.css", - SYSCONFDIR "/xdg/waybar/style.css", - "./resources/style.css", - }) - : style; - if (css_file.empty() || config_file.empty()) { - throw std::runtime_error("Missing required resources files"); - } - spdlog::info("Resources files: {}, {}", config_file, css_file); - return {config_file, css_file}; -} - -auto waybar::Client::setupConfig(const std::string &config_file, int depth) -> void { - if (depth > 100) { - throw std::runtime_error("Aborting due to likely recursive include in config files"); - } - std::ifstream file(config_file); - if (!file.is_open()) { - throw std::runtime_error("Can't open config file"); - } - std::string str((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - util::JsonParser parser; - Json::Value tmp_config_ = parser.parse(str); - if (tmp_config_.isArray()) { - for (auto &config_part : tmp_config_) { - resolveConfigIncludes(config_part, depth); - } - } else { - resolveConfigIncludes(tmp_config_, depth); - } - mergeConfig(config_, tmp_config_); -} - -auto waybar::Client::resolveConfigIncludes(Json::Value &config, int depth) -> void { - Json::Value includes = config["include"]; - if (includes.isArray()) { - for (const auto &include : includes) { - spdlog::info("Including resource file: {}", include.asString()); - setupConfig(getValidPath({include.asString()}), ++depth); - } - } else if (includes.isString()) { - spdlog::info("Including resource file: {}", includes.asString()); - setupConfig(getValidPath({includes.asString()}), ++depth); - } -} - -auto waybar::Client::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void { - if (!a_config_) { - // For the first config - a_config_ = b_config_; - } else if (a_config_.isObject() && b_config_.isObject()) { - for (const auto &key : b_config_.getMemberNames()) { - if (a_config_[key].isObject() && b_config_[key].isObject()) { - mergeConfig(a_config_[key], b_config_[key]); - } else { - a_config_[key] = b_config_[key]; - } - } - } else if (a_config_.isArray() && b_config_.isArray()) { - // This can happen only on the top-level array of a multi-bar config - for (Json::Value::ArrayIndex i = 0; i < b_config_.size(); i++) { - if (a_config_[i].isObject() && b_config_[i].isObject()) { - mergeConfig(a_config_[i], b_config_[i]); - } - } - } else { - spdlog::error("Cannot merge config, conflicting or invalid JSON types"); - } -} - auto waybar::Client::setupCss(const std::string &css_file) -> void { css_provider_ = Gtk::CssProvider::create(); style_context_ = Gtk::StyleContext::create(); @@ -367,9 +226,8 @@ int waybar::Client::main(int argc, char *argv[]) { throw std::runtime_error("Bar need to run under Wayland"); } wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj()); - auto [config_file, css_file] = getConfigs(config, style); - setupConfig(config_file, 0); - setupCss(css_file); + config_.load(config, style); + setupCss(config_.getStyle()); bindInterfaces(); gtk_app->hold(); gtk_app->run(); diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 00000000..6c7fa179 --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,166 @@ +#include "config.hpp" + +#include +#include +#include + +#include +#include + +#include "util/json.hpp" + +#ifndef SYSCONFDIR +#define SYSCONFDIR "/etc" +#endif + +namespace waybar { + +const std::string getValidPath(const std::vector &paths) { + wordexp_t p; + + for (const std::string &path : paths) { + if (wordexp(path.c_str(), &p, 0) == 0) { + if (access(*p.we_wordv, F_OK) == 0) { + std::string result = *p.we_wordv; + wordfree(&p); + return result; + } + wordfree(&p); + } + } + + return std::string(); +} + +std::tuple getConfigs(const std::string &config, + const std::string &style) { + auto config_file = config.empty() ? getValidPath({ + "$XDG_CONFIG_HOME/waybar/config", + "$XDG_CONFIG_HOME/waybar/config.jsonc", + "$HOME/.config/waybar/config", + "$HOME/.config/waybar/config.jsonc", + "$HOME/waybar/config", + "$HOME/waybar/config.jsonc", + "/etc/xdg/waybar/config", + "/etc/xdg/waybar/config.jsonc", + SYSCONFDIR "/xdg/waybar/config", + "./resources/config", + }) + : config; + auto css_file = style.empty() ? getValidPath({ + "$XDG_CONFIG_HOME/waybar/style.css", + "$HOME/.config/waybar/style.css", + "$HOME/waybar/style.css", + "/etc/xdg/waybar/style.css", + SYSCONFDIR "/xdg/waybar/style.css", + "./resources/style.css", + }) + : style; + if (css_file.empty() || config_file.empty()) { + throw std::runtime_error("Missing required resources files"); + } + spdlog::info("Resources files: {}, {}", config_file, css_file); + return {config_file, css_file}; +} + +void Config::setupConfig(const std::string &config_file, int depth) { + if (depth > 100) { + throw std::runtime_error("Aborting due to likely recursive include in config files"); + } + std::ifstream file(config_file); + if (!file.is_open()) { + throw std::runtime_error("Can't open config file"); + } + std::string str((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + util::JsonParser parser; + Json::Value tmp_config = parser.parse(str); + if (tmp_config.isArray()) { + for (auto &config_part : tmp_config) { + resolveConfigIncludes(config_part, depth); + } + } else { + resolveConfigIncludes(tmp_config, depth); + } + mergeConfig(config_, tmp_config); +} + +void Config::resolveConfigIncludes(Json::Value &config, int depth) { + Json::Value includes = config["include"]; + if (includes.isArray()) { + for (const auto &include : includes) { + spdlog::info("Including resource file: {}", include.asString()); + setupConfig(getValidPath({include.asString()}), ++depth); + } + } else if (includes.isString()) { + spdlog::info("Including resource file: {}", includes.asString()); + setupConfig(getValidPath({includes.asString()}), ++depth); + } +} + +void Config::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) { + if (!a_config_) { + // For the first config + a_config_ = b_config_; + } else if (a_config_.isObject() && b_config_.isObject()) { + for (const auto &key : b_config_.getMemberNames()) { + if (a_config_[key].isObject() && b_config_[key].isObject()) { + mergeConfig(a_config_[key], b_config_[key]); + } else { + a_config_[key] = b_config_[key]; + } + } + } else if (a_config_.isArray() && b_config_.isArray()) { + // This can happen only on the top-level array of a multi-bar config + for (Json::Value::ArrayIndex i = 0; i < b_config_.size(); i++) { + if (a_config_[i].isObject() && b_config_[i].isObject()) { + mergeConfig(a_config_[i], b_config_[i]); + } + } + } else { + spdlog::error("Cannot merge config, conflicting or invalid JSON types"); + } +} +bool isValidOutput(const Json::Value &config, const std::string &name, + const std::string &identifier) { + if (config["output"].isArray()) { + for (auto const &output_conf : config["output"]) { + if (output_conf.isString() && + (output_conf.asString() == name || output_conf.asString() == identifier)) { + return true; + } + } + return false; + } else if (config["output"].isString()) { + auto config_output = config["output"].asString(); + if (!config_output.empty()) { + if (config_output.substr(0, 1) == "!") { + return config_output.substr(1) != name && config_output.substr(1) != identifier; + } + return config_output == name || config_output == identifier; + } + } + + return true; +} + +void Config::load(const std::string &config, const std::string &style) { + std::tie(config_file_, css_file_) = getConfigs(config, style); + setupConfig(config_file_, 0); +} + +std::vector Config::getOutputConfigs(const std::string &name, + const std::string &identifier) { + std::vector configs; + if (config_.isArray()) { + for (auto const &config : config_) { + if (config.isObject() && isValidOutput(config, name, identifier)) { + configs.push_back(config); + } + } + } else if (isValidOutput(config_, name, identifier)) { + configs.push_back(config_); + } + return configs; +} + +} // namespace waybar