From cb49650ea48b457e0b5a4572d794bbc1a73bc33a Mon Sep 17 00:00:00 2001 From: Michael Swiger Date: Sun, 22 Aug 2021 14:46:40 -0700 Subject: [PATCH 01/30] Use g_memdup2 instead of g_memdup This fixes a compile warning. See: https://discourse.gnome.org/t/port-your-module-from-g-memdup-to-g-memdup2-now/5538 --- src/modules/sni/item.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 991ccc4da..917a92a9f 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -287,7 +287,7 @@ Glib::RefPtr Item::extractPixBuf(GVariant* variant) { if (array != nullptr) { g_free(array); } - array = static_cast(g_memdup(data, size)); + array = static_cast(g_memdup2(data, size)); lwidth = width; lheight = height; } From 4f76c9bd43dd7ff8108ffe81c690fe3e6232ee6a Mon Sep 17 00:00:00 2001 From: Michael Swiger Date: Sun, 29 Aug 2021 13:11:04 -0700 Subject: [PATCH 02/30] Only use g_memdup2 for glib >= 2.68 --- src/modules/sni/item.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 917a92a9f..d9748eda1 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -287,7 +287,11 @@ Glib::RefPtr Item::extractPixBuf(GVariant* variant) { if (array != nullptr) { g_free(array); } +#if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 68 array = static_cast(g_memdup2(data, size)); +#else + array = static_cast(g_memdup(data, size)); +#endif lwidth = width; lheight = height; } From aacd0fcc652f36901632a252bd91e7f0c5f73cf1 Mon Sep 17 00:00:00 2001 From: Matan1x Date: Wed, 8 Sep 2021 17:12:30 +0300 Subject: [PATCH 03/30] round brightness --- src/modules/backlight.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index 3ebc6e7bb..fcd668c5c 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -173,7 +173,7 @@ auto waybar::modules::Backlight::update() -> void { return; } - const auto percent = best->get_max() == 0 ? 100 : best->get_actual() * 100 / best->get_max(); + const uint8_t percent = best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max()); label_.set_markup(fmt::format( format_, fmt::arg("percent", std::to_string(percent)), fmt::arg("icon", getIcon(percent)))); getState(percent); From 2c380a53caabfc831d832985becb95b69d202eed Mon Sep 17 00:00:00 2001 From: Rolf Vidar Mazunki Hoksaas Date: Thu, 9 Sep 2021 20:05:18 +0200 Subject: [PATCH 04/30] added support for the {gwaddr} variable --- Makefile | 3 +++ include/modules/network.hpp | 1 + man/waybar-network.5.scd | 2 ++ src/modules/network.cpp | 10 +++++++++- 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d7182c187..ccf050739 100644 --- a/Makefile +++ b/Makefile @@ -16,5 +16,8 @@ install: build run: build ./build/waybar +debug-run: build + ./build/waybar --log-level debug + clean: rm -rf build diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 009ae5a3e..ad28520ee 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -67,6 +67,7 @@ class Network : public ALabel { bool carrier_; std::string ifname_; std::string ipaddr_; + std::string gwaddr_; std::string netmask_; int cidr_; int32_t signal_strength_dbm_; diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index f274881ea..f8bdd65d4 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -131,6 +131,8 @@ Addressed by *network* *{ipaddr}*: The first IP of the interface. +*{gwaddr}*: The default gateway for the interface + *{netmask}*: The subnetmask corresponding to the IP. *{cidr}*: The subnetmask corresponding to the IP in CIDR notation. diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 7d0f63823..a45f2b874 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -348,6 +348,7 @@ auto waybar::modules::Network::update() -> void { fmt::arg("ifname", ifname_), fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), + fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), fmt::arg("frequency", frequency_), fmt::arg("icon", getIcon(signal_strength_, state_)), @@ -376,6 +377,7 @@ auto waybar::modules::Network::update() -> void { fmt::arg("ifname", ifname_), fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), + fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), fmt::arg("frequency", frequency_), fmt::arg("icon", getIcon(signal_strength_, state_)), @@ -409,6 +411,7 @@ void waybar::modules::Network::clearIface() { ifname_.clear(); essid_.clear(); ipaddr_.clear(); + gwaddr_.clear(); netmask_.clear(); carrier_ = false; cidr_ = 0; @@ -581,6 +584,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { break; } + char temp_gw_addr[INET6_ADDRSTRLEN]; case RTM_DELROUTE: is_del_event = true; case RTM_NEWROUTE: { @@ -595,6 +599,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { int temp_idx = -1; uint32_t priority = 0; + /* Find the message(s) concerting the main routing table, each message * corresponds to a single routing table entry. */ @@ -612,9 +617,10 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { case RTA_GATEWAY: /* The gateway of the route. * - * If someone every needs to figure out the gateway address as well, + * If someone ever needs to figure out the gateway address as well, * it's here as the attribute payload. */ + inet_ntop(net->family_, RTA_DATA(attr), temp_gw_addr, sizeof(temp_gw_addr)); has_gateway = true; break; case RTA_DST: { @@ -655,6 +661,8 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->clearIface(); net->ifid_ = temp_idx; net->route_priority = priority; + net->gwaddr_ = temp_gw_addr; + spdlog::debug("netwok: gateway {}", net->gwaddr_); spdlog::debug("network: new default route via if{} metric {}", temp_idx, priority); From 95ecff05511f71fc542429059b843ecd369854ef Mon Sep 17 00:00:00 2001 From: Rolf Vidar Mazunki Hoksaas Date: Thu, 9 Sep 2021 20:12:20 +0200 Subject: [PATCH 05/30] added example tooltip usage --- resources/config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/config b/resources/config index 87f24c043..b3157212c 100644 --- a/resources/config +++ b/resources/config @@ -117,7 +117,8 @@ "network": { // "interface": "wlp2*", // (Optional) To force the use of this interface "format-wifi": "{essid} ({signalStrength}%) ", - "format-ethernet": "{ifname}: {ipaddr}/{cidr} ", + "format-ethernet": "{ipaddr}/{cidr} ", + "tooltip-format": "{ifname} via {gwaddr} ", "format-linked": "{ifname} (No IP) ", "format-disconnected": "Disconnected ⚠", "format-alt": "{ifname}: {ipaddr}/{cidr}" From b377520a38dd0a2aa68301039e9f649d98a94f5f Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 13 Aug 2021 06:08:27 -0700 Subject: [PATCH 06/30] 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 e7fa1db02..e68e4ad05 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 000000000..bb7b90688 --- /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 835b70e35..641607d62 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 ff6e7bf29..b50faff75 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 000000000..6c7fa179f --- /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 From 4fff2eaaa0851c559fabd5b04ef08de8b9604fe1 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 15 Sep 2021 21:24:45 +0700 Subject: [PATCH 07/30] refactor(client): change config visibility to public --- include/client.hpp | 2 +- src/client.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index e68e4ad05..1124cbba9 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -28,6 +28,7 @@ class Client { struct zxdg_output_manager_v1 * xdg_output_manager = nullptr; struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = nullptr; std::vector> bars; + Config config; private: Client() = default; @@ -47,7 +48,6 @@ class Client { void handleMonitorRemoved(Glib::RefPtr monitor); void handleDeferredMonitorRemoval(Glib::RefPtr monitor); - Config config_; Glib::RefPtr style_context_; Glib::RefPtr css_provider_; std::list outputs_; diff --git a/src/client.cpp b/src/client.cpp index b50faff75..076488398 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -61,7 +61,7 @@ struct waybar::waybar_output &waybar::Client::getOutput(void *addr) { } std::vector waybar::Client::getOutputConfigs(struct waybar_output &output) { - return config_.getOutputConfigs(output.name, output.identifier); + return config.getOutputConfigs(output.name, output.identifier); } void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_output*/) { @@ -188,14 +188,14 @@ void waybar::Client::bindInterfaces() { int waybar::Client::main(int argc, char *argv[]) { bool show_help = false; bool show_version = false; - std::string config; - std::string style; + std::string config_opt; + std::string style_opt; std::string bar_id; std::string log_level; auto cli = clara::detail::Help(show_help) | clara::detail::Opt(show_version)["-v"]["--version"]("Show version") | - clara::detail::Opt(config, "config")["-c"]["--config"]("Config path") | - clara::detail::Opt(style, "style")["-s"]["--style"]("Style path") | + clara::detail::Opt(config_opt, "config")["-c"]["--config"]("Config path") | + clara::detail::Opt(style_opt, "style")["-s"]["--style"]("Style path") | clara::detail::Opt( log_level, "trace|debug|info|warning|error|critical|off")["-l"]["--log-level"]("Log level") | @@ -226,8 +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()); - config_.load(config, style); - setupCss(config_.getStyle()); + config.load(config_opt, style_opt); + setupCss(config.getStyle()); bindInterfaces(); gtk_app->hold(); gtk_app->run(); From 1f7d399b8edd3ea76612e674ee1b3fb1a365e483 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 13 Aug 2021 07:19:47 -0700 Subject: [PATCH 08/30] refactor(config): remove style handling from Config --- include/client.hpp | 1 + include/config.hpp | 16 ++++++--- src/client.cpp | 14 ++++++-- src/config.cpp | 82 ++++++++++++++++++++++------------------------ 4 files changed, 65 insertions(+), 48 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index 1124cbba9..bd80d0bd3 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -32,6 +32,7 @@ class Client { private: Client() = default; + const std::string getStyle(const std::string &style); void bindInterfaces(); void handleOutput(struct waybar_output &output); auto setupCss(const std::string &css_file) -> void; diff --git a/include/config.hpp b/include/config.hpp index bb7b90688..25b78ab2a 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -2,17 +2,26 @@ #include +#include #include +#ifndef SYSCONFDIR +#define SYSCONFDIR "/etc" +#endif + namespace waybar { class Config { public: - Config() = default; + static const std::vector CONFIG_DIRS; - void load(const std::string &config, const std::string &style); + /* Try to find any of provided names in the supported set of config directories */ + static std::optional findConfigPath( + const std::vector &names, const std::vector &dirs = CONFIG_DIRS); + + Config() = default; - const std::string &getStyle() { return css_file_; } + void load(const std::string &config); Json::Value &getConfig() { return config_; } @@ -24,7 +33,6 @@ class Config { void mergeConfig(Json::Value &a_config_, Json::Value &b_config_); std::string config_file_; - std::string css_file_; Json::Value config_; }; diff --git a/src/client.cpp b/src/client.cpp index 076488398..95f5a295f 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -151,6 +151,15 @@ void waybar::Client::handleDeferredMonitorRemoval(Glib::RefPtr mon outputs_.remove_if([&monitor](const auto &output) { return output.monitor == monitor; }); } +const std::string waybar::Client::getStyle(const std::string &style) { + auto css_file = style.empty() ? Config::findConfigPath({"style.css"}) : style; + if (!css_file) { + throw std::runtime_error("Missing required resource files"); + } + spdlog::info("Using CSS file {}", css_file.value()); + return css_file.value(); +}; + auto waybar::Client::setupCss(const std::string &css_file) -> void { css_provider_ = Gtk::CssProvider::create(); style_context_ = Gtk::StyleContext::create(); @@ -226,8 +235,9 @@ 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()); - config.load(config_opt, style_opt); - setupCss(config.getStyle()); + config.load(config_opt); + auto css_file = getStyle(style_opt); + setupCss(css_file); bindInterfaces(); gtk_app->hold(); gtk_app->run(); diff --git a/src/config.cpp b/src/config.cpp index 6c7fa179f..ed7168dc0 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -9,58 +9,51 @@ #include "util/json.hpp" -#ifndef SYSCONFDIR -#define SYSCONFDIR "/etc" -#endif - namespace waybar { -const std::string getValidPath(const std::vector &paths) { +const std::vector Config::CONFIG_DIRS = { + "$XDG_CONFIG_HOME/waybar/", + "$HOME/.config/waybar/", + "$HOME/waybar/", + "/etc/xdg/waybar/", + SYSCONFDIR "/xdg/waybar/", + "./resources/", +}; + +std::optional tryExpandPath(const std::string &path) { wordexp_t p; + 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::nullopt; +} +const std::string getValidPath(const std::vector &paths) { 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); + if (auto res = tryExpandPath(path); res) { + return res.value(); } } 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"); +std::optional Config::findConfigPath(const std::vector &names, + const std::vector &dirs) { + std::vector paths; + for (const auto &dir : dirs) { + for (const auto &name : names) { + if (auto res = tryExpandPath(dir + name); res) { + return res; + } + } } - spdlog::info("Resources files: {}, {}", config_file, css_file); - return {config_file, css_file}; + return std::nullopt; } void Config::setupConfig(const std::string &config_file, int depth) { @@ -143,8 +136,13 @@ bool isValidOutput(const Json::Value &config, const std::string &name, return true; } -void Config::load(const std::string &config, const std::string &style) { - std::tie(config_file_, css_file_) = getConfigs(config, style); +void Config::load(const std::string &config) { + auto file = config.empty() ? findConfigPath({"config", "config.jsonc"}) : config; + if (!file) { + throw std::runtime_error("Missing required resource files"); + } + config_file_ = file.value(); + spdlog::info("Using configuration file {}", config_file_); setupConfig(config_file_, 0); } From 1f16d7955d7832e2fcdc3317d42034ac0c772dac Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 13 Aug 2021 08:19:12 -0700 Subject: [PATCH 09/30] refactor(config): drop getValidPath --- src/config.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index ed7168dc0..207e1bd12 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -33,16 +33,6 @@ std::optional tryExpandPath(const std::string &path) { return std::nullopt; } -const std::string getValidPath(const std::vector &paths) { - for (const std::string &path : paths) { - if (auto res = tryExpandPath(path); res) { - return res.value(); - } - } - - return std::string(); -} - std::optional Config::findConfigPath(const std::vector &names, const std::vector &dirs) { std::vector paths; @@ -82,11 +72,11 @@ void Config::resolveConfigIncludes(Json::Value &config, int depth) { if (includes.isArray()) { for (const auto &include : includes) { spdlog::info("Including resource file: {}", include.asString()); - setupConfig(getValidPath({include.asString()}), ++depth); + setupConfig(tryExpandPath(include.asString()).value_or(""), ++depth); } } else if (includes.isString()) { spdlog::info("Including resource file: {}", includes.asString()); - setupConfig(getValidPath({includes.asString()}), ++depth); + setupConfig(tryExpandPath(includes.asString()).value_or(""), ++depth); } } From 6eba62f0600379f9d594dea1fa5c41024f55fe8e Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 13 Aug 2021 18:33:24 -0700 Subject: [PATCH 10/30] test: add build configs for catch2 --- meson.build | 9 +++++++++ meson_options.txt | 1 + subprojects/catch2.wrap | 12 ++++++++++++ test/config.cpp | 4 ++++ test/meson.build | 21 +++++++++++++++++++++ 5 files changed, 47 insertions(+) create mode 100644 subprojects/catch2.wrap create mode 100644 test/config.cpp create mode 100644 test/meson.build diff --git a/meson.build b/meson.build index 641607d62..c09fbb617 100644 --- a/meson.build +++ b/meson.build @@ -360,6 +360,15 @@ if scdoc.found() endforeach endif +catch2 = dependency( + 'catch2', + fallback: ['catch2', 'catch2_dep'], + required: get_option('tests'), +) +if catch2.found() + subdir('test') +endif + clangtidy = find_program('clang-tidy', required: false) if clangtidy.found() diff --git a/meson_options.txt b/meson_options.txt index fefb3dc33..81e44689e 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -10,3 +10,4 @@ option('mpd', type: 'feature', value: 'auto', description: 'Enable support for t option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support') option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL') option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio') +option('tests', type: 'feature', value: 'auto', description: 'Enable tests') diff --git a/subprojects/catch2.wrap b/subprojects/catch2.wrap new file mode 100644 index 000000000..356c40636 --- /dev/null +++ b/subprojects/catch2.wrap @@ -0,0 +1,12 @@ +[wrap-file] +directory = Catch2-2.13.3 +source_url = https://github.com/catchorg/Catch2/archive/v2.13.3.zip +source_filename = Catch2-2.13.3.zip +source_hash = 1804feb72bc15c0856b4a43aa586c661af9c3685a75973b6a8fc0b950c7cfd13 +patch_url = https://github.com/mesonbuild/catch2/releases/download/2.13.3-2/catch2.zip +patch_filename = catch2-2.13.3-2-wrap.zip +patch_hash = 21b590ab8c65b593ad5ee8f8e5b822bf9877b2c2672f97fbb52459751053eadf + +[provide] +catch2 = catch2_dep + diff --git a/test/config.cpp b/test/config.cpp new file mode 100644 index 000000000..41180bb74 --- /dev/null +++ b/test/config.cpp @@ -0,0 +1,4 @@ +#define CATCH_CONFIG_MAIN +#include "config.hpp" + +#include diff --git a/test/meson.build b/test/meson.build new file mode 100644 index 000000000..85b9771f5 --- /dev/null +++ b/test/meson.build @@ -0,0 +1,21 @@ +test_inc = include_directories('../include') +test_dep = [ + catch2, + fmt, + jsoncpp, + spdlog, +] + +config_test = executable( + 'config_test', + 'config.cpp', + '../src/config.cpp', + dependencies: test_dep, + include_directories: test_inc, +) + +test( + 'Configuration test', + config_test, + workdir: meson.source_root(), +) From 9f3b34e4d9c47eae5c3a7179e70952d5c6ba3f61 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 13 Aug 2021 18:50:36 -0700 Subject: [PATCH 11/30] test: validate configuration load --- test/config.cpp | 74 ++++++++++++++++++++++++++++++++++++++ test/config/include-1.json | 6 ++++ test/config/include-2.json | 3 ++ test/config/include.json | 4 +++ test/config/multi.json | 25 +++++++++++++ test/config/simple.json | 5 +++ 6 files changed, 117 insertions(+) create mode 100644 test/config/include-1.json create mode 100644 test/config/include-2.json create mode 100644 test/config/include.json create mode 100644 test/config/multi.json create mode 100644 test/config/simple.json diff --git a/test/config.cpp b/test/config.cpp index 41180bb74..78ea0e136 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -2,3 +2,77 @@ #include "config.hpp" #include + +TEST_CASE("Load simple config", "[config]") { + waybar::Config conf; + conf.load("test/config/simple.json"); + + SECTION("validate the config data") { + auto& data = conf.getConfig(); + REQUIRE(data["layer"].asString() == "top"); + REQUIRE(data["height"].asInt() == 30); + } + SECTION("select configs for configured output") { + auto configs = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0"); + REQUIRE(configs.size() == 1); + } + SECTION("select configs for missing output") { + auto configs = conf.getOutputConfigs("HDMI-1", "Fake HDMI output #1"); + REQUIRE(configs.empty()); + } +} + +TEST_CASE("Load config with multiple bars", "[config]") { + waybar::Config conf; + conf.load("test/config/multi.json"); + + SECTION("select multiple configs #1") { + auto data = conf.getOutputConfigs("DP-0", "Fake DisplayPort output #0"); + REQUIRE(data.size() == 3); + REQUIRE(data[0]["layer"].asString() == "bottom"); + REQUIRE(data[0]["height"].asInt() == 20); + REQUIRE(data[1]["layer"].asString() == "top"); + REQUIRE(data[1]["position"].asString() == "bottom"); + REQUIRE(data[1]["height"].asInt() == 21); + REQUIRE(data[2]["layer"].asString() == "overlay"); + REQUIRE(data[2]["position"].asString() == "right"); + REQUIRE(data[2]["height"].asInt() == 23); + } + SECTION("select multiple configs #2") { + auto data = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0"); + REQUIRE(data.size() == 2); + REQUIRE(data[0]["layer"].asString() == "bottom"); + REQUIRE(data[0]["height"].asInt() == 20); + REQUIRE(data[1]["layer"].asString() == "overlay"); + REQUIRE(data[1]["position"].asString() == "right"); + REQUIRE(data[1]["height"].asInt() == 23); + } + SECTION("select single config by output description") { + auto data = conf.getOutputConfigs("HDMI-1", "Fake HDMI output #1"); + REQUIRE(data.size() == 1); + REQUIRE(data[0]["layer"].asString() == "overlay"); + REQUIRE(data[0]["position"].asString() == "left"); + REQUIRE(data[0]["height"].asInt() == 22); + } +} + +TEST_CASE("Load simple config with include", "[config]") { + waybar::Config conf; + conf.load("test/config/include.json"); + + SECTION("validate the config data") { + auto& data = conf.getConfig(); + REQUIRE(data["layer"].asString() == "bottom"); + REQUIRE(data["height"].asInt() == 30); + // config override behavior: preserve value from the top config + REQUIRE(data["position"].asString() == "top"); + } + SECTION("select configs for configured output") { + auto configs = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0"); + REQUIRE(configs.size() == 1); + } + SECTION("select configs for missing output") { + auto configs = conf.getOutputConfigs("HDMI-1", "Fake HDMI output #1"); + REQUIRE(configs.empty()); + } +} diff --git a/test/config/include-1.json b/test/config/include-1.json new file mode 100644 index 000000000..853111c7d --- /dev/null +++ b/test/config/include-1.json @@ -0,0 +1,6 @@ +{ + "layer": "top", + "position": "bottom", + "height": 30, + "output": ["HDMI-0", "DP-0"] +} diff --git a/test/config/include-2.json b/test/config/include-2.json new file mode 100644 index 000000000..741194f1c --- /dev/null +++ b/test/config/include-2.json @@ -0,0 +1,3 @@ +{ + "layer": "bottom" +} diff --git a/test/config/include.json b/test/config/include.json new file mode 100644 index 000000000..098cae01a --- /dev/null +++ b/test/config/include.json @@ -0,0 +1,4 @@ +{ + "include": ["test/config/include-1.json", "test/config/include-2.json"], + "position": "top" +} diff --git a/test/config/multi.json b/test/config/multi.json new file mode 100644 index 000000000..ed43a39ea --- /dev/null +++ b/test/config/multi.json @@ -0,0 +1,25 @@ +[ + { + "layer": "bottom", + "height": 20, + "output": ["HDMI-0", "DP-0"] + }, + { + "position": "bottom", + "layer": "top", + "height": 21, + "output": ["DP-0"] + }, + { + "position": "left", + "layer": "overlay", + "height": 22, + "output": "Fake HDMI output #1" + }, + { + "position": "right", + "layer": "overlay", + "height": 23, + "output": "!HDMI-1" + } +] diff --git a/test/config/simple.json b/test/config/simple.json new file mode 100644 index 000000000..1cb1e3a19 --- /dev/null +++ b/test/config/simple.json @@ -0,0 +1,5 @@ +{ + "layer": "top", + "height": 30, + "output": ["HDMI-0", "DP-0"] +} From 8912bd3ed0734aeaf4db961e1cfab28a42ac4cb2 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 16 Aug 2021 21:11:46 +0700 Subject: [PATCH 12/30] test: multi-bar config with includes --- test/config.cpp | 34 ++++++++++++++++++++++++++++++ test/config/include-multi-0.json | 9 ++++++++ test/config/include-multi-1.json | 8 +++++++ test/config/include-multi-2.json | 9 ++++++++ test/config/include-multi-3-0.json | 8 +++++++ test/config/include-multi-3.json | 9 ++++++++ test/config/include-multi.json | 16 ++++++++++++++ 7 files changed, 93 insertions(+) create mode 100644 test/config/include-multi-0.json create mode 100644 test/config/include-multi-1.json create mode 100644 test/config/include-multi-2.json create mode 100644 test/config/include-multi-3-0.json create mode 100644 test/config/include-multi-3.json create mode 100644 test/config/include-multi.json diff --git a/test/config.cpp b/test/config.cpp index 78ea0e136..0dc0b42a6 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -76,3 +76,37 @@ TEST_CASE("Load simple config with include", "[config]") { REQUIRE(configs.empty()); } } + +TEST_CASE("Load multiple bar config with include", "[config]") { + waybar::Config conf; + conf.load("test/config/include-multi.json"); + + SECTION("bar config with sole include") { + auto data = conf.getOutputConfigs("OUT-0", "Fake ouptut #0"); + REQUIRE(data.size() == 1); + REQUIRE(data[0]["height"].asInt() == 20); + } + + SECTION("bar config with output and include") { + auto data = conf.getOutputConfigs("OUT-1", "Fake output #1"); + REQUIRE(data.size() == 1); + REQUIRE(data[0]["height"].asInt() == 21); + } + + SECTION("bar config with output override") { + auto data = conf.getOutputConfigs("OUT-2", "Fake output #2"); + REQUIRE(data.size() == 1); + REQUIRE(data[0]["height"].asInt() == 22); + } + + SECTION("multiple levels of include") { + auto data = conf.getOutputConfigs("OUT-3", "Fake output #3"); + REQUIRE(data.size() == 1); + REQUIRE(data[0]["height"].asInt() == 23); + } + + auto& data = conf.getConfig(); + REQUIRE(data.isArray()); + REQUIRE(data.size() == 4); + REQUIRE(data[0]["output"].asString() == "OUT-0"); +} diff --git a/test/config/include-multi-0.json b/test/config/include-multi-0.json new file mode 100644 index 000000000..87b6cabee --- /dev/null +++ b/test/config/include-multi-0.json @@ -0,0 +1,9 @@ +[ + { + "output": "OUT-0", + "height": 20 + }, + {}, + {}, + {} +] diff --git a/test/config/include-multi-1.json b/test/config/include-multi-1.json new file mode 100644 index 000000000..d816a0fde --- /dev/null +++ b/test/config/include-multi-1.json @@ -0,0 +1,8 @@ +[ + {}, + { + "height": 21 + }, + {}, + {} +] diff --git a/test/config/include-multi-2.json b/test/config/include-multi-2.json new file mode 100644 index 000000000..47616ef1a --- /dev/null +++ b/test/config/include-multi-2.json @@ -0,0 +1,9 @@ +[ + {}, + {}, + { + "output": "OUT-1", + "height": 22 + }, + {} +] diff --git a/test/config/include-multi-3-0.json b/test/config/include-multi-3-0.json new file mode 100644 index 000000000..3f4da0c92 --- /dev/null +++ b/test/config/include-multi-3-0.json @@ -0,0 +1,8 @@ +[ + {}, + {}, + {}, + { + "height": 23 + } +] diff --git a/test/config/include-multi-3.json b/test/config/include-multi-3.json new file mode 100644 index 000000000..d09518943 --- /dev/null +++ b/test/config/include-multi-3.json @@ -0,0 +1,9 @@ +[ + {}, + {}, + {}, + { + "output": "OUT-3", + "include": "test/config/include-multi-3-0.json" + } +] diff --git a/test/config/include-multi.json b/test/config/include-multi.json new file mode 100644 index 000000000..e128aba58 --- /dev/null +++ b/test/config/include-multi.json @@ -0,0 +1,16 @@ +[ + { + "include": "test/config/include-multi-0.json" + }, + { + "output": "OUT-1", + "include": "test/config/include-multi-1.json" + }, + { + "output": "OUT-2", + "include": "test/config/include-multi-2.json" + }, + { + "include": "test/config/include-multi-3.json" + } +] From ccc60b42459b29fed266759bd668b697636d5253 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 14 Sep 2021 12:16:37 +0700 Subject: [PATCH 13/30] refactor(config): more sensible multi-bar include behavior --- include/config.hpp | 2 +- src/config.cpp | 20 +++++++------------- test/config.cpp | 3 ++- test/config/include-multi-0.json | 13 ++++--------- test/config/include-multi-1.json | 11 +++-------- test/config/include-multi-2.json | 13 ++++--------- test/config/include-multi-3-0.json | 11 +++-------- test/config/include-multi-3.json | 13 ++++--------- 8 files changed, 28 insertions(+), 58 deletions(-) diff --git a/include/config.hpp b/include/config.hpp index 25b78ab2a..82d55995c 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -28,7 +28,7 @@ class Config { std::vector getOutputConfigs(const std::string &name, const std::string &identifier); private: - void setupConfig(const std::string &config_file, int depth); + void setupConfig(Json::Value &dst, const std::string &config_file, int depth); void resolveConfigIncludes(Json::Value &config, int depth); void mergeConfig(Json::Value &a_config_, Json::Value &b_config_); diff --git a/src/config.cpp b/src/config.cpp index 207e1bd12..63f18c267 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -46,7 +46,7 @@ std::optional Config::findConfigPath(const std::vector return std::nullopt; } -void Config::setupConfig(const std::string &config_file, int depth) { +void Config::setupConfig(Json::Value &dst, const std::string &config_file, int depth) { if (depth > 100) { throw std::runtime_error("Aborting due to likely recursive include in config files"); } @@ -64,7 +64,7 @@ void Config::setupConfig(const std::string &config_file, int depth) { } else { resolveConfigIncludes(tmp_config, depth); } - mergeConfig(config_, tmp_config); + mergeConfig(dst, tmp_config); } void Config::resolveConfigIncludes(Json::Value &config, int depth) { @@ -72,11 +72,11 @@ void Config::resolveConfigIncludes(Json::Value &config, int depth) { if (includes.isArray()) { for (const auto &include : includes) { spdlog::info("Including resource file: {}", include.asString()); - setupConfig(tryExpandPath(include.asString()).value_or(""), ++depth); + setupConfig(config, tryExpandPath(include.asString()).value_or(""), ++depth); } } else if (includes.isString()) { spdlog::info("Including resource file: {}", includes.asString()); - setupConfig(tryExpandPath(includes.asString()).value_or(""), ++depth); + setupConfig(config, tryExpandPath(includes.asString()).value_or(""), ++depth); } } @@ -88,17 +88,11 @@ void Config::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) { for (const auto &key : b_config_.getMemberNames()) { if (a_config_[key].isObject() && b_config_[key].isObject()) { mergeConfig(a_config_[key], b_config_[key]); - } else { + } else if (a_config_[key].isNull()) { + // do not allow overriding value set by top or previously included config 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"); } @@ -133,7 +127,7 @@ void Config::load(const std::string &config) { } config_file_ = file.value(); spdlog::info("Using configuration file {}", config_file_); - setupConfig(config_file_, 0); + setupConfig(config_, config_file_, 0); } std::vector Config::getOutputConfigs(const std::string &name, diff --git a/test/config.cpp b/test/config.cpp index 0dc0b42a6..343a1c116 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -62,7 +62,8 @@ TEST_CASE("Load simple config with include", "[config]") { SECTION("validate the config data") { auto& data = conf.getConfig(); - REQUIRE(data["layer"].asString() == "bottom"); + // config override behavior: preserve first included value + REQUIRE(data["layer"].asString() == "top"); REQUIRE(data["height"].asInt() == 30); // config override behavior: preserve value from the top config REQUIRE(data["position"].asString() == "top"); diff --git a/test/config/include-multi-0.json b/test/config/include-multi-0.json index 87b6cabee..a4c3fc178 100644 --- a/test/config/include-multi-0.json +++ b/test/config/include-multi-0.json @@ -1,9 +1,4 @@ -[ - { - "output": "OUT-0", - "height": 20 - }, - {}, - {}, - {} -] +{ + "output": "OUT-0", + "height": 20 +} diff --git a/test/config/include-multi-1.json b/test/config/include-multi-1.json index d816a0fde..2b28d6c52 100644 --- a/test/config/include-multi-1.json +++ b/test/config/include-multi-1.json @@ -1,8 +1,3 @@ -[ - {}, - { - "height": 21 - }, - {}, - {} -] +{ + "height": 21 +} diff --git a/test/config/include-multi-2.json b/test/config/include-multi-2.json index 47616ef1a..f74c2b4e6 100644 --- a/test/config/include-multi-2.json +++ b/test/config/include-multi-2.json @@ -1,9 +1,4 @@ -[ - {}, - {}, - { - "output": "OUT-1", - "height": 22 - }, - {} -] +{ + "output": "OUT-1", + "height": 22 +} diff --git a/test/config/include-multi-3-0.json b/test/config/include-multi-3-0.json index 3f4da0c92..11cdd3f97 100644 --- a/test/config/include-multi-3-0.json +++ b/test/config/include-multi-3-0.json @@ -1,8 +1,3 @@ -[ - {}, - {}, - {}, - { - "height": 23 - } -] +{ + "height": 23 +} diff --git a/test/config/include-multi-3.json b/test/config/include-multi-3.json index d09518943..309fe15e5 100644 --- a/test/config/include-multi-3.json +++ b/test/config/include-multi-3.json @@ -1,9 +1,4 @@ -[ - {}, - {}, - {}, - { - "output": "OUT-3", - "include": "test/config/include-multi-3-0.json" - } -] +{ + "output": "OUT-3", + "include": "test/config/include-multi-3-0.json" +} From 0c1d3e30b62dc7c94b7ae32b97089309de7ff956 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 15 Sep 2021 22:14:12 +0700 Subject: [PATCH 14/30] fix(config): preserve explicit null when merging objects --- src/config.cpp | 9 +++++++-- test/config.cpp | 2 ++ test/config/include-1.json | 3 ++- test/config/include.json | 3 ++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index 63f18c267..63149cbdb 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -1,5 +1,6 @@ #include "config.hpp" +#include #include #include #include @@ -86,11 +87,15 @@ void Config::mergeConfig(Json::Value &a_config_, Json::Value &b_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()) { + // [] creates key with default value. Use `get` to avoid that. + if (a_config_.get(key, Json::Value::nullSingleton()).isObject() && + b_config_[key].isObject()) { mergeConfig(a_config_[key], b_config_[key]); - } else if (a_config_[key].isNull()) { + } else if (!a_config_.isMember(key)) { // do not allow overriding value set by top or previously included config a_config_[key] = b_config_[key]; + } else { + spdlog::trace("Option {} is already set; ignoring value {}", key, b_config_[key]); } } } else { diff --git a/test/config.cpp b/test/config.cpp index 343a1c116..f09f5da6c 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -67,6 +67,8 @@ TEST_CASE("Load simple config with include", "[config]") { REQUIRE(data["height"].asInt() == 30); // config override behavior: preserve value from the top config REQUIRE(data["position"].asString() == "top"); + // config override behavior: explicit null is still a value and should be preserved + REQUIRE((data.isMember("nullOption") && data["nullOption"].isNull())); } SECTION("select configs for configured output") { auto configs = conf.getOutputConfigs("HDMI-0", "Fake HDMI output #0"); diff --git a/test/config/include-1.json b/test/config/include-1.json index 853111c7d..7c47a8829 100644 --- a/test/config/include-1.json +++ b/test/config/include-1.json @@ -2,5 +2,6 @@ "layer": "top", "position": "bottom", "height": 30, - "output": ["HDMI-0", "DP-0"] + "output": ["HDMI-0", "DP-0"], + "nullOption": "not null" } diff --git a/test/config/include.json b/test/config/include.json index 098cae01a..c46aaf241 100644 --- a/test/config/include.json +++ b/test/config/include.json @@ -1,4 +1,5 @@ { "include": ["test/config/include-1.json", "test/config/include-2.json"], - "position": "top" + "position": "top", + "nullOption": null } From d7d606b72152da08c4a68dcbb36b92f070419159 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 15 Sep 2021 22:00:23 +0700 Subject: [PATCH 15/30] doc: update documentation for 'include' --- man/waybar.5.scd.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 9dc6925fe..997a48d85 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -87,8 +87,9 @@ Also a minimal example configuration can be found on the at the bottom of this m *include* ++ typeof: string|array ++ - Paths to additional configuration files. In case of duplicate options, the including file's value takes precedence. Make sure to avoid circular imports. - For a multi-bar config, specify at least an empty object for each bar also in every file being included. + Paths to additional configuration files. + Each file can contain a single object with any of the bar configuration options. In case of duplicate options, the first defined value takes precedence, i.e. including file -> first included file -> etc. Nested includes are permitted, but make sure to avoid circular imports. + For a multi-bar config, the include directive affects only current bar configuration object. # MODULE FORMAT From 5991bbb741fed4756e6efdec473b56a5c9767840 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 14 Sep 2021 14:02:38 +0700 Subject: [PATCH 16/30] ci: run unit-tests --- .github/workflows/freebsd.yml | 7 ++++--- .github/workflows/linux.yml | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index f02c9b571..8af7a04a4 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -15,9 +15,10 @@ jobs: export CPPFLAGS=-isystem/usr/local/include LDFLAGS=-L/usr/local/lib # sndio sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf pkg install -y git # subprojects/date - pkg install -y evdev-proto gtk-layer-shell gtkmm30 jsoncpp libdbusmenu \ - libevdev libfmt libmpdclient libudev-devd meson pkgconf pulseaudio \ - scdoc sndio spdlog + pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \ + libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \ + pkgconf pulseaudio scdoc sndio spdlog run: | meson build -Dman-pages=enabled ninja -C build + meson test -C build --no-rebuild --print-errorlogs --suite waybar diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index e550e20b3..d4efbf8c2 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -23,3 +23,5 @@ jobs: run: meson -Dman-pages=enabled build - name: build run: ninja -C build + - name: test + run: meson test -C build --no-rebuild --print-errorlogs --suite waybar From 4bf577e89bfc2c7c816cda99e1b5a68e63ae9d5a Mon Sep 17 00:00:00 2001 From: Darkclainer Date: Fri, 17 Sep 2021 21:18:21 +0300 Subject: [PATCH 17/30] Add CPU usage for every core --- include/modules/cpu.hpp | 11 ++++++----- src/modules/cpu/common.cpp | 32 +++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index 866b5af7c..1fe1199be 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -19,11 +20,11 @@ class Cpu : public ALabel { auto update() -> void; private: - double getCpuLoad(); - std::tuple getCpuUsage(); - std::tuple getCpuFrequency(); - std::vector> parseCpuinfo(); - std::vector parseCpuFrequencies(); + double getCpuLoad(); + std::tuple, std::string> getCpuUsage(); + std::tuple getCpuFrequency(); + std::vector> parseCpuinfo(); + std::vector parseCpuFrequencies(); std::vector> prev_times_; diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index 767cde92c..5869e13fd 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -17,7 +17,8 @@ auto waybar::modules::Cpu::update() -> void { label_.set_tooltip_text(tooltip); } auto format = format_; - auto state = getState(cpu_usage); + auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0]; + auto state = getState(total_usage); if (!state.empty() && config_["format-" + state].isString()) { format = config_["format-" + state].asString(); } @@ -27,13 +28,22 @@ auto waybar::modules::Cpu::update() -> void { } else { event_box_.show(); auto icons = std::vector{state}; - label_.set_markup(fmt::format(format, - fmt::arg("load", cpu_load), - fmt::arg("usage", cpu_usage), - fmt::arg("icon", getIcon(cpu_usage, icons)), - fmt::arg("max_frequency", max_frequency), - fmt::arg("min_frequency", min_frequency), - fmt::arg("avg_frequency", avg_frequency))); + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("load", cpu_load)); + store.push_back(fmt::arg("load", cpu_load)); + store.push_back(fmt::arg("usage", total_usage)); + store.push_back(fmt::arg("icon", getIcon(total_usage, icons))); + store.push_back(fmt::arg("max_frequency", max_frequency)); + store.push_back(fmt::arg("min_frequency", min_frequency)); + store.push_back(fmt::arg("avg_frequency", avg_frequency)); + for (size_t i = 1; i < cpu_usage.size(); ++i) { + auto core_i = i - 1; + auto core_format = fmt::format("usage{}", core_i); + store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i])); + auto icon_format = fmt::format("icon{}", core_i); + store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons))); + } + label_.set_markup(fmt::vformat(format, store)); } // Call parent update @@ -48,14 +58,14 @@ double waybar::modules::Cpu::getCpuLoad() { throw std::runtime_error("Can't get Cpu load"); } -std::tuple waybar::modules::Cpu::getCpuUsage() { +std::tuple, std::string> waybar::modules::Cpu::getCpuUsage() { if (prev_times_.empty()) { prev_times_ = parseCpuinfo(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } std::vector> curr_times = parseCpuinfo(); std::string tooltip; - uint16_t usage = 0; + std::vector usage; for (size_t i = 0; i < curr_times.size(); ++i) { auto [curr_idle, curr_total] = curr_times[i]; auto [prev_idle, prev_total] = prev_times_[i]; @@ -63,11 +73,11 @@ std::tuple waybar::modules::Cpu::getCpuUsage() { const float delta_total = curr_total - prev_total; uint16_t tmp = 100 * (1 - delta_idle / delta_total); if (i == 0) { - usage = tmp; tooltip = fmt::format("Total: {}%", tmp); } else { tooltip = tooltip + fmt::format("\nCore{}: {}%", i - 1, tmp); } + usage.push_back(tmp); } prev_times_ = curr_times; return {usage, tooltip}; From 8da940f929bd8c06a30fd3d02de53e6fac2ac5f8 Mon Sep 17 00:00:00 2001 From: Darkclainer Date: Fri, 17 Sep 2021 22:22:14 +0300 Subject: [PATCH 18/30] Update man pages for cpu usage --- man/waybar-cpu.5.scd | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index 679ca2ced..2e0d6c71d 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -82,7 +82,9 @@ The *cpu* module displays the current cpu utilization. *{load}*: Current cpu load. -*{usage}*: Current cpu usage. +*{usage}*: Current overall cpu usage. + +*{usage*{n}*}*: Current cpu core n usage. Cores are numbered from zero, so first core will be {usage0} and 4th will be {usage3}. *{avg_frequency}*: Current cpu average frequency (based on all cores) in GHz. @@ -90,7 +92,13 @@ The *cpu* module displays the current cpu utilization. *{min_frequency}*: Current cpu min frequency (based on the core with the lowest frequency) in GHz. -# EXAMPLE +*{icon}*: Icon for overall cpu usage. + +*{icon*{n}*}*: Icon for cpu core n usage. Use like {icon0}. + +# EXAMPLES + +Basic configuration: ``` "cpu": { @@ -100,6 +108,16 @@ The *cpu* module displays the current cpu utilization. } ``` +Cpu usage per core rendered as icons: + +``` +"cpu": { + "interval": 1, + "format": "{icon0}{icon1}{icon2}{icon3} {usage:>2}% ", + "format-icons": ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"], +}, +``` + # STYLE - *#cpu* From 5f083193e4202e616d587b53050111fef1fa031f Mon Sep 17 00:00:00 2001 From: mazunki Date: Sat, 18 Sep 2021 01:12:58 +0200 Subject: [PATCH 19/30] fixed tab indentation to spaces, removed debug --- src/modules/network.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index a45f2b874..0fccf0915 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -661,9 +661,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->clearIface(); net->ifid_ = temp_idx; net->route_priority = priority; - net->gwaddr_ = temp_gw_addr; - spdlog::debug("netwok: gateway {}", net->gwaddr_); - + net->gwaddr_ = temp_gw_addr; spdlog::debug("network: new default route via if{} metric {}", temp_idx, priority); /* Ask ifname associated with temp_idx as well as carrier status */ From 13239417d817460eee8dd46bd09ddae0fc8dcf5a Mon Sep 17 00:00:00 2001 From: mazunki Date: Sat, 18 Sep 2021 01:20:16 +0200 Subject: [PATCH 20/30] fixed wrong dependency for make target --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ccf050739..94f8ee6e9 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ install: build run: build ./build/waybar -debug-run: build +debug-run: build-debug ./build/waybar --log-level debug clean: From 6142dfba6a1ffc5e08ac68df172291d926fa8b03 Mon Sep 17 00:00:00 2001 From: mazunki Date: Sat, 18 Sep 2021 01:51:16 +0200 Subject: [PATCH 21/30] updated original debug message with gateway ip, similar, yet not identical to default via 10.13.37.100 dev enp7s0 metric 2 10.13.37.0/24 dev enp7s0 proto kernel scope link src 10.13.37.97 's output --- src/modules/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 0fccf0915..3b9fb1870 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -662,7 +662,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->ifid_ = temp_idx; net->route_priority = priority; net->gwaddr_ = temp_gw_addr; - spdlog::debug("network: new default route via if{} metric {}", temp_idx, priority); + spdlog::debug("network: new default route via {} on if{} metric {}", temp_gw_addr, temp_idx, priority); /* Ask ifname associated with temp_idx as well as carrier status */ struct ifinfomsg ifinfo_hdr = { From 1c91c71dcde2d77506989b407eefcd18b20f34a3 Mon Sep 17 00:00:00 2001 From: mazunki Date: Sat, 18 Sep 2021 01:51:16 +0200 Subject: [PATCH 22/30] updated original debug message with gateway ip, similar, yet not identical to `ip route` --- src/modules/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 0fccf0915..3b9fb1870 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -662,7 +662,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->ifid_ = temp_idx; net->route_priority = priority; net->gwaddr_ = temp_gw_addr; - spdlog::debug("network: new default route via if{} metric {}", temp_idx, priority); + spdlog::debug("network: new default route via {} on if{} metric {}", temp_gw_addr, temp_idx, priority); /* Ask ifname associated with temp_idx as well as carrier status */ struct ifinfomsg ifinfo_hdr = { From 67c730293885684a954632a70fe041abd2288685 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 18 Sep 2021 13:50:16 +0200 Subject: [PATCH 23/30] Revert "Add CPU usage for every core" --- include/modules/cpu.hpp | 11 +++++------ man/waybar-cpu.5.scd | 22 ++-------------------- src/modules/cpu/common.cpp | 32 +++++++++++--------------------- 3 files changed, 18 insertions(+), 47 deletions(-) diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index 1fe1199be..866b5af7c 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -20,11 +19,11 @@ class Cpu : public ALabel { auto update() -> void; private: - double getCpuLoad(); - std::tuple, std::string> getCpuUsage(); - std::tuple getCpuFrequency(); - std::vector> parseCpuinfo(); - std::vector parseCpuFrequencies(); + double getCpuLoad(); + std::tuple getCpuUsage(); + std::tuple getCpuFrequency(); + std::vector> parseCpuinfo(); + std::vector parseCpuFrequencies(); std::vector> prev_times_; diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index 2e0d6c71d..679ca2ced 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -82,9 +82,7 @@ The *cpu* module displays the current cpu utilization. *{load}*: Current cpu load. -*{usage}*: Current overall cpu usage. - -*{usage*{n}*}*: Current cpu core n usage. Cores are numbered from zero, so first core will be {usage0} and 4th will be {usage3}. +*{usage}*: Current cpu usage. *{avg_frequency}*: Current cpu average frequency (based on all cores) in GHz. @@ -92,13 +90,7 @@ The *cpu* module displays the current cpu utilization. *{min_frequency}*: Current cpu min frequency (based on the core with the lowest frequency) in GHz. -*{icon}*: Icon for overall cpu usage. - -*{icon*{n}*}*: Icon for cpu core n usage. Use like {icon0}. - -# EXAMPLES - -Basic configuration: +# EXAMPLE ``` "cpu": { @@ -108,16 +100,6 @@ Basic configuration: } ``` -Cpu usage per core rendered as icons: - -``` -"cpu": { - "interval": 1, - "format": "{icon0}{icon1}{icon2}{icon3} {usage:>2}% ", - "format-icons": ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"], -}, -``` - # STYLE - *#cpu* diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index 5869e13fd..767cde92c 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -17,8 +17,7 @@ auto waybar::modules::Cpu::update() -> void { label_.set_tooltip_text(tooltip); } auto format = format_; - auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0]; - auto state = getState(total_usage); + auto state = getState(cpu_usage); if (!state.empty() && config_["format-" + state].isString()) { format = config_["format-" + state].asString(); } @@ -28,22 +27,13 @@ auto waybar::modules::Cpu::update() -> void { } else { event_box_.show(); auto icons = std::vector{state}; - fmt::dynamic_format_arg_store store; - store.push_back(fmt::arg("load", cpu_load)); - store.push_back(fmt::arg("load", cpu_load)); - store.push_back(fmt::arg("usage", total_usage)); - store.push_back(fmt::arg("icon", getIcon(total_usage, icons))); - store.push_back(fmt::arg("max_frequency", max_frequency)); - store.push_back(fmt::arg("min_frequency", min_frequency)); - store.push_back(fmt::arg("avg_frequency", avg_frequency)); - for (size_t i = 1; i < cpu_usage.size(); ++i) { - auto core_i = i - 1; - auto core_format = fmt::format("usage{}", core_i); - store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i])); - auto icon_format = fmt::format("icon{}", core_i); - store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons))); - } - label_.set_markup(fmt::vformat(format, store)); + label_.set_markup(fmt::format(format, + fmt::arg("load", cpu_load), + fmt::arg("usage", cpu_usage), + fmt::arg("icon", getIcon(cpu_usage, icons)), + fmt::arg("max_frequency", max_frequency), + fmt::arg("min_frequency", min_frequency), + fmt::arg("avg_frequency", avg_frequency))); } // Call parent update @@ -58,14 +48,14 @@ double waybar::modules::Cpu::getCpuLoad() { throw std::runtime_error("Can't get Cpu load"); } -std::tuple, std::string> waybar::modules::Cpu::getCpuUsage() { +std::tuple waybar::modules::Cpu::getCpuUsage() { if (prev_times_.empty()) { prev_times_ = parseCpuinfo(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } std::vector> curr_times = parseCpuinfo(); std::string tooltip; - std::vector usage; + uint16_t usage = 0; for (size_t i = 0; i < curr_times.size(); ++i) { auto [curr_idle, curr_total] = curr_times[i]; auto [prev_idle, prev_total] = prev_times_[i]; @@ -73,11 +63,11 @@ std::tuple, std::string> waybar::modules::Cpu::getCpuUsage const float delta_total = curr_total - prev_total; uint16_t tmp = 100 * (1 - delta_idle / delta_total); if (i == 0) { + usage = tmp; tooltip = fmt::format("Total: {}%", tmp); } else { tooltip = tooltip + fmt::format("\nCore{}: {}%", i - 1, tmp); } - usage.push_back(tmp); } prev_times_ = curr_times; return {usage, tooltip}; From fe547901fae1f536d047b468d8c35d3afb70a6e2 Mon Sep 17 00:00:00 2001 From: Gavin Beatty Date: Sat, 18 Sep 2021 15:28:27 -0500 Subject: [PATCH 24/30] sway/language: remove tabs, indent with 2 spaces --- src/modules/sway/language.cpp | 74 +++++++++++++++++------------------ 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index 85dc769ec..186fa4bb0 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -20,15 +20,15 @@ const std::string Language::XKB_ACTIVE_LAYOUT_NAME_KEY = "xkb_active_layout_name Language::Language(const std::string& id, const Json::Value& config) : ALabel(config, "language", id, "{}", 0, true) { is_variant_displayed = format_.find("{variant}") != std::string::npos; - if (format_.find("{}") != std::string::npos || format_.find("{short}") != std::string::npos) { - displayed_short_flag |= static_cast(DispayedShortFlag::ShortName); - } - if (format_.find("{shortDescription}") != std::string::npos) { - displayed_short_flag |= static_cast(DispayedShortFlag::ShortDescription); - } - if (config.isMember("tooltip-format")) { - tooltip_format_ = config["tooltip-format"].asString(); - } + if (format_.find("{}") != std::string::npos || format_.find("{short}") != std::string::npos) { + displayed_short_flag |= static_cast(DispayedShortFlag::ShortName); + } + if (format_.find("{shortDescription}") != std::string::npos) { + displayed_short_flag |= static_cast(DispayedShortFlag::ShortDescription); + } + if (config.isMember("tooltip-format")) { + tooltip_format_ = config["tooltip-format"].asString(); + } ipc_.subscribe(R"(["input"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Language::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Language::onCmd)); @@ -102,16 +102,16 @@ auto Language::update() -> void { fmt::arg("variant", layout_.variant))); label_.set_markup(display_layout); if (tooltipEnabled()) { - if (tooltip_format_ != "") { - auto tooltip_display_layout = trim(fmt::format(tooltip_format_, - fmt::arg("short", layout_.short_name), - fmt::arg("shortDescription", layout_.short_description), - fmt::arg("long", layout_.full_name), - fmt::arg("variant", layout_.variant))); - label_.set_tooltip_markup(tooltip_display_layout); - } else { - label_.set_tooltip_markup(display_layout); - } + if (tooltip_format_ != "") { + auto tooltip_display_layout = trim(fmt::format(tooltip_format_, + fmt::arg("short", layout_.short_name), + fmt::arg("shortDescription", layout_.short_description), + fmt::arg("long", layout_.full_name), + fmt::arg("variant", layout_.variant))); + label_.set_tooltip_markup(tooltip_display_layout); + } else { + label_.set_tooltip_markup(display_layout); + } } event_box_.show(); @@ -126,7 +126,7 @@ auto Language::set_current_layout(std::string current_layout) -> void { auto Language::init_layouts_map(const std::vector& used_layouts) -> void { std::map> found_by_short_names; - XKBContext xkb_context; + XKBContext xkb_context; auto layout = xkb_context.next_layout(); for (; layout != nullptr; layout = xkb_context.next_layout()) { if (std::find(used_layouts.begin(), used_layouts.end(), layout->full_name) == @@ -161,15 +161,15 @@ auto Language::init_layouts_map(const std::vector& used_layouts) -> if (short_name_to_number_map.count(used_layout->short_name) == 0) { short_name_to_number_map[used_layout->short_name] = 1; } - - if (displayed_short_flag != static_cast(0)) { - int& number = short_name_to_number_map[used_layout->short_name]; - used_layout->short_name = - used_layout->short_name + std::to_string(number); - used_layout->short_description = - used_layout->short_description + std::to_string(number); - ++number; - } + + if (displayed_short_flag != static_cast(0)) { + int& number = short_name_to_number_map[used_layout->short_name]; + used_layout->short_name = + used_layout->short_name + std::to_string(number); + used_layout->short_description = + used_layout->short_description + std::to_string(number); + ++number; + } } } @@ -195,14 +195,14 @@ auto Language::XKBContext::next_layout() -> Layout* { auto variant_ = rxkb_layout_get_variant(xkb_layout_); std::string variant = variant_ == nullptr ? "" : std::string(variant_); auto short_description_ = rxkb_layout_get_brief(xkb_layout_); - std::string short_description; - if (short_description_ != nullptr) { - short_description = std::string(short_description_); - base_layouts_by_name_.emplace(name, xkb_layout_); - } else { - auto base_layout = base_layouts_by_name_[name]; - short_description = base_layout == nullptr ? "" : std::string(rxkb_layout_get_brief(base_layout)); - } + std::string short_description; + if (short_description_ != nullptr) { + short_description = std::string(short_description_); + base_layouts_by_name_.emplace(name, xkb_layout_); + } else { + auto base_layout = base_layouts_by_name_[name]; + short_description = base_layout == nullptr ? "" : std::string(rxkb_layout_get_brief(base_layout)); + } delete layout_; layout_ = new Layout{description, name, variant, short_description}; return layout_; From 6e5a0bc80a3301f4f30ce58909eadb7d54bd739e Mon Sep 17 00:00:00 2001 From: Darkclainer Date: Sun, 19 Sep 2021 13:41:59 +0300 Subject: [PATCH 25/30] Add cpu usage for every core --- include/modules/cpu.hpp | 10 +++++----- man/waybar-cpu.5.scd | 22 ++++++++++++++++++-- src/modules/cpu/common.cpp | 41 ++++++++++++++++++++++++++++---------- 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index 866b5af7c..7e32a43f9 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -19,11 +19,11 @@ class Cpu : public ALabel { auto update() -> void; private: - double getCpuLoad(); - std::tuple getCpuUsage(); - std::tuple getCpuFrequency(); - std::vector> parseCpuinfo(); - std::vector parseCpuFrequencies(); + double getCpuLoad(); + std::tuple, std::string> getCpuUsage(); + std::tuple getCpuFrequency(); + std::vector> parseCpuinfo(); + std::vector parseCpuFrequencies(); std::vector> prev_times_; diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index 679ca2ced..2e0d6c71d 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -82,7 +82,9 @@ The *cpu* module displays the current cpu utilization. *{load}*: Current cpu load. -*{usage}*: Current cpu usage. +*{usage}*: Current overall cpu usage. + +*{usage*{n}*}*: Current cpu core n usage. Cores are numbered from zero, so first core will be {usage0} and 4th will be {usage3}. *{avg_frequency}*: Current cpu average frequency (based on all cores) in GHz. @@ -90,7 +92,13 @@ The *cpu* module displays the current cpu utilization. *{min_frequency}*: Current cpu min frequency (based on the core with the lowest frequency) in GHz. -# EXAMPLE +*{icon}*: Icon for overall cpu usage. + +*{icon*{n}*}*: Icon for cpu core n usage. Use like {icon0}. + +# EXAMPLES + +Basic configuration: ``` "cpu": { @@ -100,6 +108,16 @@ The *cpu* module displays the current cpu utilization. } ``` +Cpu usage per core rendered as icons: + +``` +"cpu": { + "interval": 1, + "format": "{icon0}{icon1}{icon2}{icon3} {usage:>2}% ", + "format-icons": ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"], +}, +``` + # STYLE - *#cpu* diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index 767cde92c..4cf67eba5 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -1,5 +1,14 @@ #include "modules/cpu.hpp" +// In the 80000 version of fmt library authors decided to optimize imports +// and moved declarations required for fmt::dynamic_format_arg_store in new +// header fmt/args.h +#if (FMT_VERSION >= 80000) +#include +#else +#include +#endif + waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config) : ALabel(config, "cpu", id, "{usage}%", 10) { thread_ = [this] { @@ -17,7 +26,8 @@ auto waybar::modules::Cpu::update() -> void { label_.set_tooltip_text(tooltip); } auto format = format_; - auto state = getState(cpu_usage); + auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0]; + auto state = getState(total_usage); if (!state.empty() && config_["format-" + state].isString()) { format = config_["format-" + state].asString(); } @@ -27,13 +37,22 @@ auto waybar::modules::Cpu::update() -> void { } else { event_box_.show(); auto icons = std::vector{state}; - label_.set_markup(fmt::format(format, - fmt::arg("load", cpu_load), - fmt::arg("usage", cpu_usage), - fmt::arg("icon", getIcon(cpu_usage, icons)), - fmt::arg("max_frequency", max_frequency), - fmt::arg("min_frequency", min_frequency), - fmt::arg("avg_frequency", avg_frequency))); + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("load", cpu_load)); + store.push_back(fmt::arg("load", cpu_load)); + store.push_back(fmt::arg("usage", total_usage)); + store.push_back(fmt::arg("icon", getIcon(total_usage, icons))); + store.push_back(fmt::arg("max_frequency", max_frequency)); + store.push_back(fmt::arg("min_frequency", min_frequency)); + store.push_back(fmt::arg("avg_frequency", avg_frequency)); + for (size_t i = 1; i < cpu_usage.size(); ++i) { + auto core_i = i - 1; + auto core_format = fmt::format("usage{}", core_i); + store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i])); + auto icon_format = fmt::format("icon{}", core_i); + store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons))); + } + label_.set_markup(fmt::vformat(format, store)); } // Call parent update @@ -48,14 +67,14 @@ double waybar::modules::Cpu::getCpuLoad() { throw std::runtime_error("Can't get Cpu load"); } -std::tuple waybar::modules::Cpu::getCpuUsage() { +std::tuple, std::string> waybar::modules::Cpu::getCpuUsage() { if (prev_times_.empty()) { prev_times_ = parseCpuinfo(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } std::vector> curr_times = parseCpuinfo(); std::string tooltip; - uint16_t usage = 0; + std::vector usage; for (size_t i = 0; i < curr_times.size(); ++i) { auto [curr_idle, curr_total] = curr_times[i]; auto [prev_idle, prev_total] = prev_times_[i]; @@ -63,11 +82,11 @@ std::tuple waybar::modules::Cpu::getCpuUsage() { const float delta_total = curr_total - prev_total; uint16_t tmp = 100 * (1 - delta_idle / delta_total); if (i == 0) { - usage = tmp; tooltip = fmt::format("Total: {}%", tmp); } else { tooltip = tooltip + fmt::format("\nCore{}: {}%", i - 1, tmp); } + usage.push_back(tmp); } prev_times_ = curr_times; return {usage, tooltip}; From f638fe473a54e62d0ec41e143b4cf616ce388099 Mon Sep 17 00:00:00 2001 From: Ryan Walklin Date: Wed, 22 Sep 2021 11:43:25 +1200 Subject: [PATCH 26/30] Update catch2 dependency 2.13.3 -> 2.13.7 --- subprojects/catch2.wrap | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/subprojects/catch2.wrap b/subprojects/catch2.wrap index 356c40636..c82b310fb 100644 --- a/subprojects/catch2.wrap +++ b/subprojects/catch2.wrap @@ -1,11 +1,11 @@ [wrap-file] -directory = Catch2-2.13.3 -source_url = https://github.com/catchorg/Catch2/archive/v2.13.3.zip -source_filename = Catch2-2.13.3.zip -source_hash = 1804feb72bc15c0856b4a43aa586c661af9c3685a75973b6a8fc0b950c7cfd13 -patch_url = https://github.com/mesonbuild/catch2/releases/download/2.13.3-2/catch2.zip -patch_filename = catch2-2.13.3-2-wrap.zip -patch_hash = 21b590ab8c65b593ad5ee8f8e5b822bf9877b2c2672f97fbb52459751053eadf +directory = Catch2-2.13.7 +source_url = https://github.com/catchorg/Catch2/archive/v2.13.7.zip +source_filename = Catch2-2.13.7.zip +source_hash = 3f3ccd90ad3a8fbb1beeb15e6db440ccdcbebe378dfd125d07a1f9a587a927e9 +patch_filename = catch2_2.13.7-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/catch2_2.13.7-1/get_patch +patch_hash = 2f7369645d747e5bd866317ac1dd4c3d04dc97d3aad4fc6b864bdf75d3b57158 [provide] catch2 = catch2_dep From fbedc3d133d627b8ff4f8c9fe5744739bf8dd0b3 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 19 Sep 2021 18:30:41 +0300 Subject: [PATCH 27/30] fix(tray): fix visibility of Passive items `show_all` call from `Tray::update` attempts to walk the widget tree and make every widget visible. Since we control individual tray item visibility based on `Status` SNI property, we don't want that to happen. Modify `Tray::update` to control the visibility of a whole tray module only and ensure that the children of `Item` are still visible when necessary. --- src/modules/sni/item.cpp | 1 + src/modules/sni/tray.cpp | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index d9748eda1..b504c8d07 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -62,6 +62,7 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &Item::handleClick)); event_box.signal_scroll_event().connect(sigc::mem_fun(*this, &Item::handleScroll)); // initial visibility + event_box.show_all(); event_box.set_visible(show_passive_); cancellable_ = Gio::Cancellable::create(); diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index e73c9eb43..70b0d375f 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -35,11 +35,8 @@ void Tray::onRemove(std::unique_ptr& item) { } auto Tray::update() -> void { - if (box_.get_children().empty()) { - box_.hide(); - } else { - box_.show_all(); - } + // Show tray only when items are availale + box_.set_visible(!box_.get_children().empty()); // Call parent update AModule::update(); } From f18eb71ad7b6b1a523fdbb8416bbc713213b6ccd Mon Sep 17 00:00:00 2001 From: Elyes HAOUAS Date: Sat, 2 Oct 2021 18:13:17 +0200 Subject: [PATCH 28/30] Fix spelling errors Signed-off-by: Elyes HAOUAS --- resources/custom_modules/mediaplayer.py | 2 +- src/modules/clock.cpp | 2 +- src/modules/network.cpp | 2 +- src/modules/sni/tray.cpp | 2 +- test/config.cpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/custom_modules/mediaplayer.py b/resources/custom_modules/mediaplayer.py index cf3df4b68..fa9aa58ba 100755 --- a/resources/custom_modules/mediaplayer.py +++ b/resources/custom_modules/mediaplayer.py @@ -79,7 +79,7 @@ def signal_handler(sig, frame): def parse_arguments(): parser = argparse.ArgumentParser() - # Increase verbosity with every occurence of -v + # Increase verbosity with every occurrence of -v parser.add_argument('-v', '--verbose', action='count', default=0) # Define for which player we're listening diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 82c570102..0c38cb6d2 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -16,7 +16,7 @@ using waybar::modules::waybar_time; waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), fixed_time_zone_(false) { if (config_["timezone"].isString()) { - spdlog::warn("As using a timezone, some format args may be missing as the date library havn't got a release since 2018."); + spdlog::warn("As using a timezone, some format args may be missing as the date library haven't got a release since 2018."); time_zone_ = date::locate_zone(config_["timezone"].asString()); fixed_time_zone_ = true; } diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 3b9fb1870..99ccd8e0a 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -469,7 +469,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { } if (!is_del_event && ifi->ifi_index == net->ifid_) { - // Update inferface information + // Update interface information if (net->ifname_.empty() && ifname != NULL) { std::string new_ifname (ifname, ifname_len); net->ifname_ = new_ifname; diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index 70b0d375f..c32c0d6a3 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -35,7 +35,7 @@ void Tray::onRemove(std::unique_ptr& item) { } auto Tray::update() -> void { - // Show tray only when items are availale + // Show tray only when items are available box_.set_visible(!box_.get_children().empty()); // Call parent update AModule::update(); diff --git a/test/config.cpp b/test/config.cpp index f09f5da6c..edd6d6b85 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -85,7 +85,7 @@ TEST_CASE("Load multiple bar config with include", "[config]") { conf.load("test/config/include-multi.json"); SECTION("bar config with sole include") { - auto data = conf.getOutputConfigs("OUT-0", "Fake ouptut #0"); + auto data = conf.getOutputConfigs("OUT-0", "Fake output #0"); REQUIRE(data.size() == 1); REQUIRE(data[0]["height"].asInt() == 20); } From 99723845976fb577a057b86e36b502c966bf254f Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sat, 2 Oct 2021 18:35:38 +0200 Subject: [PATCH 29/30] sway/window: include floating_nodes when considering window count for class --- src/modules/sway/window.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index b67795858..fc81b2cf3 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -68,15 +68,22 @@ auto Window::update() -> void { int leafNodesInWorkspace(const Json::Value& node) { auto const& nodes = node["nodes"]; - if(nodes.empty()) { + auto const& floating_nodes = node["floating_nodes"]; + if(nodes.empty() && floating_nodes.empty()) { if(node["type"] == "workspace") return 0; else return 1; } int sum = 0; - for(auto const& node : nodes) - sum += leafNodesInWorkspace(node); + if (!nodes.empty()) { + for(auto const& node : nodes) + sum += leafNodesInWorkspace(node); + } + if (!floating_nodes.empty()) { + for(auto const& node : floating_nodes) + sum += leafNodesInWorkspace(node); + } return sum; } From 174db444d6a6a78814e0c93430755d92312832a0 Mon Sep 17 00:00:00 2001 From: Sergey Mishin Date: Sun, 3 Oct 2021 03:27:54 +0000 Subject: [PATCH 30/30] Fix Clock crash on empty string in timezones field Also fixed timezones behavior: now waybar starting with the first timezone in timezones list and falling back to timezone field only if timezones omit or has no elements. --- include/modules/clock.hpp | 1 + man/waybar-clock.5.scd | 3 ++- src/modules/clock.cpp | 30 +++++++++++++++++++----------- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index b3e2634f7..17752e4d4 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -37,6 +37,7 @@ class Clock : public ALabel { auto calendar_text(const waybar_time& wtime) -> std::string; auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void; auto first_day_of_week() -> date::weekday; + bool setTimeZone(Json::Value zone_name); }; } // namespace waybar::modules diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index 28688ee9d..2c901d2de 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -24,7 +24,8 @@ The *clock* module displays the current date and time. *timezone*: ++ typeof: string ++ default: inferred local timezone ++ - The timezone to display the time in, e.g. America/New_York. + The timezone to display the time in, e.g. America/New_York. ++ + This field will be ignored if *timezones* field is set and have at least one value. *timezones*: ++ typeof: list of strings ++ diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 0c38cb6d2..7c94c457b 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -15,10 +15,14 @@ using waybar::modules::waybar_time; waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), fixed_time_zone_(false) { - if (config_["timezone"].isString()) { + if (config_["timezones"].isArray() && !config_["timezones"].empty()) { + time_zone_idx_ = 0; + setTimeZone(config_["timezones"][time_zone_idx_]); + } else { + setTimeZone(config_["timezone"]); + } + if (fixed_time_zone_) { spdlog::warn("As using a timezone, some format args may be missing as the date library haven't got a release since 2018."); - time_zone_ = date::locate_zone(config_["timezone"].asString()); - fixed_time_zone_ = true; } if (config_["locale"].isString()) { @@ -72,6 +76,17 @@ auto waybar::modules::Clock::update() -> void { ALabel::update(); } +bool waybar::modules::Clock::setTimeZone(Json::Value zone_name) { + if (!zone_name.isString() || zone_name.asString().empty()) { + fixed_time_zone_ = false; + return false; + } + + time_zone_ = date::locate_zone(zone_name.asString()); + fixed_time_zone_ = true; + return true; +} + bool waybar::modules::Clock::handleScroll(GdkEventScroll *e) { // defer to user commands if set if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { @@ -92,14 +107,7 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll *e) { } else { time_zone_idx_ = time_zone_idx_ == 0 ? nr_zones - 1 : time_zone_idx_ - 1; } - auto zone_name = config_["timezones"][time_zone_idx_]; - if (!zone_name.isString() || zone_name.empty()) { - fixed_time_zone_ = false; - } else { - time_zone_ = date::locate_zone(zone_name.asString()); - fixed_time_zone_ = true; - } - + setTimeZone(config_["timezones"][time_zone_idx_]); update(); return true; }