diff --git a/include/factory.hpp b/include/factory.hpp index 90d0ac1d..cb25078d 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -38,6 +38,9 @@ #endif #if defined(HAVE_CPU_LINUX) || defined(HAVE_CPU_BSD) #include "modules/cpu.hpp" +#include "modules/cpu_frequency.hpp" +#include "modules/cpu_usage.hpp" +#include "modules/load.hpp" #endif #include "modules/idle_inhibitor.hpp" #if defined(HAVE_MEMORY_LINUX) || defined(HAVE_MEMORY_BSD) diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index a5235486..7f78c165 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -21,12 +21,6 @@ class Cpu : public ALabel { auto update() -> void override; private: - double getCpuLoad(); - std::tuple, std::string> getCpuUsage(); - std::tuple getCpuFrequency(); - std::vector> parseCpuinfo(); - std::vector parseCpuFrequencies(); - std::vector> prev_times_; util::SleeperThread thread_; diff --git a/include/modules/cpu_frequency.hpp b/include/modules/cpu_frequency.hpp new file mode 100644 index 00000000..49ca1b86 --- /dev/null +++ b/include/modules/cpu_frequency.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include "ALabel.hpp" +#include "util/sleeper_thread.hpp" + +namespace waybar::modules { + +class CpuFrequency : public ALabel { + public: + CpuFrequency(const std::string&, const Json::Value&); + virtual ~CpuFrequency() = default; + auto update() -> void override; + + // This is a static member because it is also used by the cpu module. + static std::tuple getCpuFrequency(); + + private: + static std::vector parseCpuFrequencies(); + + util::SleeperThread thread_; +}; + +} // namespace waybar::modules diff --git a/include/modules/cpu_usage.hpp b/include/modules/cpu_usage.hpp new file mode 100644 index 00000000..4349f705 --- /dev/null +++ b/include/modules/cpu_usage.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include "ALabel.hpp" +#include "util/sleeper_thread.hpp" + +namespace waybar::modules { + +class CpuUsage : public ALabel { + public: + CpuUsage(const std::string&, const Json::Value&); + virtual ~CpuUsage() = default; + auto update() -> void override; + + // This is a static member because it is also used by the cpu module. + static std::tuple, std::string> getCpuUsage(std::vector>&); + + private: + static std::vector> parseCpuinfo(); + + std::vector> prev_times_; + + util::SleeperThread thread_; +}; + +} // namespace waybar::modules diff --git a/include/modules/load.hpp b/include/modules/load.hpp new file mode 100644 index 00000000..2c4ce610 --- /dev/null +++ b/include/modules/load.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include "ALabel.hpp" +#include "util/sleeper_thread.hpp" + +namespace waybar::modules { + +class Load : public ALabel { + public: + Load(const std::string&, const Json::Value&); + virtual ~Load() = default; + auto update() -> void override; + + // This is a static member because it is also used by the cpu module. + static std::tuple getLoad(); + + private: + + util::SleeperThread thread_; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index b06786ba..bb9abdeb 100644 --- a/meson.build +++ b/meson.build @@ -164,6 +164,7 @@ src_files = files( 'src/modules/disk.cpp', 'src/modules/idle_inhibitor.cpp', 'src/modules/image.cpp', + 'src/modules/load.cpp', 'src/modules/temperature.cpp', 'src/modules/user.cpp', 'src/ASlider.cpp', @@ -189,8 +190,11 @@ if is_linux add_project_arguments('-DHAVE_MEMORY_LINUX', language: 'cpp') src_files += files( 'src/modules/battery.cpp', - 'src/modules/cpu/common.cpp', - 'src/modules/cpu/linux.cpp', + 'src/modules/cpu.cpp', + 'src/modules/cpu_frequency/common.cpp', + 'src/modules/cpu_frequency/linux.cpp', + 'src/modules/cpu_usage/common.cpp', + 'src/modules/cpu_usage/linux.cpp', 'src/modules/memory/common.cpp', 'src/modules/memory/linux.cpp', ) @@ -198,8 +202,11 @@ elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd add_project_arguments('-DHAVE_CPU_BSD', language: 'cpp') add_project_arguments('-DHAVE_MEMORY_BSD', language: 'cpp') src_files += files( - 'src/modules/cpu/bsd.cpp', - 'src/modules/cpu/common.cpp', + 'src/modules/cpu.cpp', + 'src/modules/cpu_frequency/bsd.cpp', + 'src/modules/cpu_frequency/common.cpp', + 'src/modules/cpu_usage/bsd.cpp', + 'src/modules/cpu_usage/common.cpp', 'src/modules/memory/bsd.cpp', 'src/modules/memory/common.cpp', ) diff --git a/src/factory.cpp b/src/factory.cpp index 6f8d7b40..aaf46036 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -107,6 +107,17 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "cpu") { return new waybar::modules::Cpu(id, config_[name]); } +#if defined(HAVE_CPU_LINUX) + if (ref == "cpu_frequency") { + return new waybar::modules::CpuFrequency(id, config_[name]); + } +#endif + if (ref == "cpu_usage") { + return new waybar::modules::CpuUsage(id, config_[name]); + } + if (ref == "load") { + return new waybar::modules::Load(id, config_[name]); + } #endif if (ref == "clock") { return new waybar::modules::Clock(id, config_[name]); diff --git a/src/modules/cpu.cpp b/src/modules/cpu.cpp new file mode 100644 index 00000000..0703eaf7 --- /dev/null +++ b/src/modules/cpu.cpp @@ -0,0 +1,63 @@ +#include "modules/cpu.hpp" + +#include "modules/cpu_frequency.hpp" +#include "modules/cpu_usage.hpp" +#include "modules/load.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] { + dp.emit(); + thread_.sleep_for(interval_); + }; +} + +auto waybar::modules::Cpu::update() -> void { + // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both + auto [load1, load5, load15] = Load::getLoad(); + auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_); + auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency(); + if (tooltipEnabled()) { + label_.set_tooltip_text(tooltip); + } + auto format = format_; + 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(); + } + + if (format.empty()) { + event_box_.hide(); + } else { + event_box_.show(); + auto icons = std::vector{state}; + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("load", load1)); + 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 + ALabel::update(); +} diff --git a/src/modules/cpu_frequency/bsd.cpp b/src/modules/cpu_frequency/bsd.cpp new file mode 100644 index 00000000..c837c1fd --- /dev/null +++ b/src/modules/cpu_frequency/bsd.cpp @@ -0,0 +1,15 @@ +#include + +#include // NAN + +#include "modules/cpu_frequency.hpp" + +std::vector waybar::modules::CpuFrequency::parseCpuFrequencies() { + static std::vector frequencies; + if (frequencies.empty()) { + spdlog::warn( + "cpu/bsd: parseCpuFrequencies is not implemented, expect garbage in {*_frequency}"); + frequencies.push_back(NAN); + } + return frequencies; +} diff --git a/src/modules/cpu_frequency/common.cpp b/src/modules/cpu_frequency/common.cpp new file mode 100644 index 00000000..e47364ba --- /dev/null +++ b/src/modules/cpu_frequency/common.cpp @@ -0,0 +1,67 @@ +#include "modules/cpu_frequency.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::CpuFrequency::CpuFrequency(const std::string& id, const Json::Value& config) + : ALabel(config, "cpu_frequency", id, "{avg_frequency}", 10) { + thread_ = [this] { + dp.emit(); + thread_.sleep_for(interval_); + }; +} + +auto waybar::modules::CpuFrequency::update() -> void { + // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both + auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency(); + if (tooltipEnabled()) { + auto tooltip = + fmt::format("Minimum frequency: {}\nAverage frequency: {}\nMaximum frequency: {}\n", + min_frequency, avg_frequency, max_frequency); + label_.set_tooltip_text(tooltip); + } + auto format = format_; + auto state = getState(avg_frequency); + if (!state.empty() && config_["format-" + state].isString()) { + format = config_["format-" + state].asString(); + } + + if (format.empty()) { + event_box_.hide(); + } else { + event_box_.show(); + auto icons = std::vector{state}; + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("icon", getIcon(avg_frequency, 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)); + label_.set_markup(fmt::vformat(format, store)); + } + + // Call parent update + ALabel::update(); +} + +std::tuple waybar::modules::CpuFrequency::getCpuFrequency() { + std::vector frequencies = CpuFrequency::parseCpuFrequencies(); + if (frequencies.empty()) { + return {0.f, 0.f, 0.f}; + } + auto [min, max] = std::minmax_element(std::begin(frequencies), std::end(frequencies)); + float avg_frequency = + std::accumulate(std::begin(frequencies), std::end(frequencies), 0.0) / frequencies.size(); + + // Round frequencies with double decimal precision to get GHz + float max_frequency = std::ceil(*max / 10.0) / 100.0; + float min_frequency = std::ceil(*min / 10.0) / 100.0; + avg_frequency = std::ceil(avg_frequency / 10.0) / 100.0; + + return {max_frequency, min_frequency, avg_frequency}; +} diff --git a/src/modules/cpu/linux.cpp b/src/modules/cpu_frequency/linux.cpp similarity index 61% rename from src/modules/cpu/linux.cpp rename to src/modules/cpu_frequency/linux.cpp index e9b18d70..1f368789 100644 --- a/src/modules/cpu/linux.cpp +++ b/src/modules/cpu_frequency/linux.cpp @@ -1,36 +1,8 @@ #include -#include "modules/cpu.hpp" +#include "modules/cpu_frequency.hpp" -std::vector> waybar::modules::Cpu::parseCpuinfo() { - const std::string data_dir_ = "/proc/stat"; - std::ifstream info(data_dir_); - if (!info.is_open()) { - throw std::runtime_error("Can't open " + data_dir_); - } - std::vector> cpuinfo; - std::string line; - while (getline(info, line)) { - if (line.substr(0, 3).compare("cpu") != 0) { - break; - } - std::stringstream sline(line.substr(5)); - std::vector times; - for (size_t time = 0; sline >> time; times.push_back(time)) - ; - - size_t idle_time = 0; - size_t total_time = 0; - if (times.size() >= 4) { - idle_time = times[3]; - total_time = std::accumulate(times.begin(), times.end(), 0); - } - cpuinfo.emplace_back(idle_time, total_time); - } - return cpuinfo; -} - -std::vector waybar::modules::Cpu::parseCpuFrequencies() { +std::vector waybar::modules::CpuFrequency::parseCpuFrequencies() { const std::string file_path_ = "/proc/cpuinfo"; std::ifstream info(file_path_); if (!info.is_open()) { diff --git a/src/modules/cpu/bsd.cpp b/src/modules/cpu_usage/bsd.cpp similarity index 86% rename from src/modules/cpu/bsd.cpp rename to src/modules/cpu_usage/bsd.cpp index 5eb767d9..c987a770 100644 --- a/src/modules/cpu/bsd.cpp +++ b/src/modules/cpu_usage/bsd.cpp @@ -8,7 +8,7 @@ #include // NAN #include // malloc -#include "modules/cpu.hpp" +#include "modules/cpu_usage.hpp" #if defined(__NetBSD__) || defined(__OpenBSD__) #include @@ -27,7 +27,7 @@ typedef uint64_t pcp_time_t; typedef long pcp_time_t; #endif -std::vector> waybar::modules::Cpu::parseCpuinfo() { +std::vector> waybar::modules::CpuUsage::parseCpuinfo() { cp_time_t sum_cp_time[CPUSTATES]; size_t sum_sz = sizeof(sum_cp_time); int ncpu = sysconf(_SC_NPROCESSORS_CONF); @@ -100,13 +100,3 @@ std::vector> waybar::modules::Cpu::parseCpuinfo() { free(cp_time); return cpuinfo; } - -std::vector waybar::modules::Cpu::parseCpuFrequencies() { - static std::vector frequencies; - if (frequencies.empty()) { - spdlog::warn( - "cpu/bsd: parseCpuFrequencies is not implemented, expect garbage in {*_frequency}"); - frequencies.push_back(NAN); - } - return frequencies; -} diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu_usage/common.cpp similarity index 55% rename from src/modules/cpu/common.cpp rename to src/modules/cpu_usage/common.cpp index 8fedf842..4e36f48e 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu_usage/common.cpp @@ -1,4 +1,4 @@ -#include "modules/cpu.hpp" +#include "modules/cpu_usage.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 @@ -9,19 +9,17 @@ #include #endif -waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config) - : ALabel(config, "cpu", id, "{usage}%", 10) { +waybar::modules::CpuUsage::CpuUsage(const std::string& id, const Json::Value& config) + : ALabel(config, "cpu_usage", id, "{usage}%", 10) { thread_ = [this] { dp.emit(); thread_.sleep_for(interval_); }; } -auto waybar::modules::Cpu::update() -> void { +auto waybar::modules::CpuUsage::update() -> void { // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both - auto cpu_load = getCpuLoad(); - auto [cpu_usage, tooltip] = getCpuUsage(); - auto [max_frequency, min_frequency, avg_frequency] = getCpuFrequency(); + auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_); if (tooltipEnabled()) { label_.set_tooltip_text(tooltip); } @@ -38,12 +36,8 @@ auto waybar::modules::Cpu::update() -> void { 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("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); @@ -58,25 +52,18 @@ auto waybar::modules::Cpu::update() -> void { ALabel::update(); } -double waybar::modules::Cpu::getCpuLoad() { - double load[1]; - if (getloadavg(load, 1) != -1) { - return std::ceil(load[0] * 100.0) / 100.0; - } - throw std::runtime_error("Can't get Cpu load"); -} - -std::tuple, std::string> waybar::modules::Cpu::getCpuUsage() { - if (prev_times_.empty()) { - prev_times_ = parseCpuinfo(); +std::tuple, std::string> waybar::modules::CpuUsage::getCpuUsage( + std::vector>& prev_times) { + if (prev_times.empty()) { + prev_times = CpuUsage::parseCpuinfo(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - std::vector> curr_times = parseCpuinfo(); + std::vector> curr_times = CpuUsage::parseCpuinfo(); std::string tooltip; 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]; + auto [prev_idle, prev_total] = prev_times[i]; const float delta_idle = curr_idle - prev_idle; const float delta_total = curr_total - prev_total; uint16_t tmp = 100 * (1 - delta_idle / delta_total); @@ -87,23 +74,6 @@ std::tuple, std::string> waybar::modules::Cpu::getCpuUsage } usage.push_back(tmp); } - prev_times_ = curr_times; + prev_times = curr_times; return {usage, tooltip}; } - -std::tuple waybar::modules::Cpu::getCpuFrequency() { - std::vector frequencies = parseCpuFrequencies(); - if (frequencies.empty()) { - return {0.f, 0.f, 0.f}; - } - auto [min, max] = std::minmax_element(std::begin(frequencies), std::end(frequencies)); - float avg_frequency = - std::accumulate(std::begin(frequencies), std::end(frequencies), 0.0) / frequencies.size(); - - // Round frequencies with double decimal precision to get GHz - float max_frequency = std::ceil(*max / 10.0) / 100.0; - float min_frequency = std::ceil(*min / 10.0) / 100.0; - avg_frequency = std::ceil(avg_frequency / 10.0) / 100.0; - - return {max_frequency, min_frequency, avg_frequency}; -} diff --git a/src/modules/cpu_usage/linux.cpp b/src/modules/cpu_usage/linux.cpp new file mode 100644 index 00000000..28930c1c --- /dev/null +++ b/src/modules/cpu_usage/linux.cpp @@ -0,0 +1,31 @@ +#include + +#include "modules/cpu_usage.hpp" + +std::vector> waybar::modules::CpuUsage::parseCpuinfo() { + const std::string data_dir_ = "/proc/stat"; + std::ifstream info(data_dir_); + if (!info.is_open()) { + throw std::runtime_error("Can't open " + data_dir_); + } + std::vector> cpuinfo; + std::string line; + while (getline(info, line)) { + if (line.substr(0, 3).compare("cpu") != 0) { + break; + } + std::stringstream sline(line.substr(5)); + std::vector times; + for (size_t time = 0; sline >> time; times.push_back(time)) + ; + + size_t idle_time = 0; + size_t total_time = 0; + if (times.size() >= 4) { + idle_time = times[3]; + total_time = std::accumulate(times.begin(), times.end(), 0); + } + cpuinfo.emplace_back(idle_time, total_time); + } + return cpuinfo; +} diff --git a/src/modules/load.cpp b/src/modules/load.cpp new file mode 100644 index 00000000..69a37b4e --- /dev/null +++ b/src/modules/load.cpp @@ -0,0 +1,61 @@ +#include "modules/load.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::Load::Load(const std::string& id, const Json::Value& config) + : ALabel(config, "load", id, "{load1}", 10) { + thread_ = [this] { + dp.emit(); + thread_.sleep_for(interval_); + }; +} + +auto waybar::modules::Load::update() -> void { + // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both + auto [load1, load5, load15] = Load::getLoad(); + if (tooltipEnabled()) { + auto tooltip = fmt::format("Load 1: {}\nLoad 5: {}\nLoad 15: {}", load1, load5, load15); + label_.set_tooltip_text(tooltip); + } + auto format = format_; + auto state = getState(load1); + if (!state.empty() && config_["format-" + state].isString()) { + format = config_["format-" + state].asString(); + } + + if (format.empty()) { + event_box_.hide(); + } else { + event_box_.show(); + auto icons = std::vector{state}; + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("load1", load1)); + store.push_back(fmt::arg("load5", load5)); + store.push_back(fmt::arg("load15", load15)); + store.push_back(fmt::arg("icon1", getIcon(load1, icons))); + store.push_back(fmt::arg("icon5", getIcon(load5, icons))); + store.push_back(fmt::arg("icon15", getIcon(load15, icons))); + label_.set_markup(fmt::vformat(format, store)); + } + + // Call parent update + ALabel::update(); +} + +std::tuple waybar::modules::Load::getLoad() { + double load[3]; + if (getloadavg(load, 3) != -1) { + double load1 = std::ceil(load[0] * 100.0) / 100.0; + double load5 = std::ceil(load[1] * 100.0) / 100.0; + double load15 = std::ceil(load[2] * 100.0) / 100.0; + return {load1, load5, load15}; + } + throw std::runtime_error("Can't get system load"); +}