Merge pull request #4762 from szbmrk/feat-multiple-disks

feat(disk)!: handle multiple disks at once
This commit is contained in:
Alexis Rouillard
2026-07-03 23:42:55 +02:00
committed by GitHub
3 changed files with 151 additions and 72 deletions
+4 -1
View File
@@ -4,6 +4,7 @@
#include <sys/statvfs.h> #include <sys/statvfs.h>
#include <fstream> #include <fstream>
#include <vector>
#include "ALabel.hpp" #include "ALabel.hpp"
#include "util/format.hpp" #include "util/format.hpp"
@@ -19,7 +20,9 @@ class Disk : public ALabel {
private: private:
util::SleeperThread thread_; util::SleeperThread thread_;
std::string path_; std::string header_;
std::vector<std::string> paths_;
std::string separator_;
std::string unit_; std::string unit_;
float calc_specific_divisor(const std::string& divisor); float calc_specific_divisor(const std::string& divisor);
+36 -9
View File
@@ -6,17 +6,12 @@ waybar - disk module
# DESCRIPTION # DESCRIPTION
The *disk* module displays the current disk space used. The *disk* module displays information of multiple disks.
# CONFIGURATION # CONFIGURATION
Addressed by *disk* Addressed by *disk*
*path*: ++
typeof: string ++
default: "/" ++
Any path residing in the filesystem or mountpoint for which the information should be displayed.
*interval*: ++ *interval*: ++
typeof: integer++ typeof: integer++
default: 30 ++ default: 30 ++
@@ -25,7 +20,7 @@ Addressed by *disk*
*format*: ++ *format*: ++
typeof: string ++ typeof: string ++
default: "{percentage_used}%" ++ default: "{percentage_used}%" ++
The format, how information should be displayed. The format, how information for each disk should be displayed.
*rotate*: ++ *rotate*: ++
typeof: integer ++ typeof: integer ++
@@ -75,6 +70,26 @@ Addressed by *disk*
typeof: string ++ typeof: string ++
Command to execute when scrolling down on the module. Command to execute when scrolling down on the module.
*path*: ++
typeof: string ++
default: "/" ++
Deprecated path of filesystem or mountpoint to monitor.
*paths*: ++
typeof: array ++
default: ["/"] ++
Array of paths residing in the filesystem or mountpoint for which the information should be displayed.
*header*: ++
typeof: string ++
default: "" ++
Text to appear before the disk information defined in the format.
*separator*: ++
typeof: string ++
default: " " ++
Separator string between multiple disk information.
*smooth-scrolling-threshold*: ++ *smooth-scrolling-threshold*: ++
typeof: double ++ typeof: double ++
Threshold to be used when scrolling. Threshold to be used when scrolling.
@@ -123,7 +138,7 @@ Addressed by *disk*
*{free}*: Amount of available disk space for normal users. Automatically selects unit based on size remaining. *{free}*: Amount of available disk space for normal users. Automatically selects unit based on size remaining.
*{path}*: The path specified in the configuration. *{path}*: The path for each disk specified in the configuration.
*{specific_total}*: Total amount of space on the disk, partition, or mountpoint in a specific unit. Defaults to bytes. *{specific_total}*: Total amount of space on the disk, partition, or mountpoint in a specific unit. Defaults to bytes.
@@ -143,10 +158,22 @@ Addressed by *disk*
``` ```
"disk": { "disk": {
"interval": 30, "interval": 30,
"format": "{percentage_free}% free on {path}",
"header": "Disks: ",
"paths": ["/", "/home"],
"separator": " ",
}
```
```
"disk": {
"interval": 30,
"paths": ["/"],
"format": "{specific_free:0.2f} GB out of {specific_total:0.2f} GB available. Alternatively {free} out of {total} available", "format": "{specific_free:0.2f} GB out of {specific_total:0.2f} GB available. Alternatively {free} out of {total} available",
"unit": "GB" "unit": "GB"
// 1434.25 GB out of 2000.00 GB available. Alternatively 1.4TiB out of 1.9TiB available.
} }
// 1434.25 GB out of 2000.00 GB available. Alternatively 1.4TiB out of 1.9TiB available.
``` ```
# STYLE # STYLE
+75 -26
View File
@@ -1,15 +1,32 @@
#include "modules/disk.hpp" #include "modules/disk.hpp"
#include <spdlog/spdlog.h>
using namespace waybar::util; using namespace waybar::util;
waybar::modules::Disk::Disk(const std::string& id, const Json::Value& config) waybar::modules::Disk::Disk(const std::string& id, const Json::Value& config)
: ALabel(config, "disk", id, "{}%", 30), path_("/") { : ALabel(config, "disk", id, "{}%", 30), header_(""), paths_(), separator_(" ") {
thread_ = [this] { thread_ = [this] {
dp.emit(); dp.emit();
thread_.sleep_for(interval_); thread_.sleep_for(interval_);
}; };
if (config["path"].isString()) { if (config["header"].isString()) {
path_ = config["path"].asString(); header_ = config["header"].asString();
}
if (config["path"].isString() && !config["paths"].isArray()) {
spdlog::warn("Disk: path is deprecated use paths instead!");
paths_.push_back(config["path"].asString());
}
if (config["paths"].isArray()) {
for (const auto& path : config["paths"]) {
paths_.push_back(path.asString());
}
}
if (!config["path"].isString() && !config["paths"].isArray()) {
paths_.emplace_back("/");
}
if (config["separator"].isString()) {
separator_ = config["separator"].asString();
} }
if (config["unit"].isString()) { if (config["unit"].isString()) {
unit_ = config["unit"].asString(); unit_ = config["unit"].asString();
@@ -17,6 +34,14 @@ waybar::modules::Disk::Disk(const std::string& id, const Json::Value& config)
} }
auto waybar::modules::Disk::update() -> void { auto waybar::modules::Disk::update() -> void {
std::string tooltip_label;
std::string label = header_;
bool had_valid_disk = false;
for (size_t i = 0; i < paths_.size(); ++i) {
const auto& path = paths_[i];
struct statvfs /* { struct statvfs /* {
unsigned long f_bsize; // filesystem block size unsigned long f_bsize; // filesystem block size
unsigned long f_frsize; // fragment size unsigned long f_frsize; // fragment size
@@ -31,7 +56,8 @@ auto waybar::modules::Disk::update() -> void {
unsigned long f_namemax; // maximum filename length unsigned long f_namemax; // maximum filename length
}; */ }; */
stats; stats;
int err = statvfs(path_.c_str(), &stats);
int err = statvfs(path.c_str(), &stats);
/* Conky options /* Conky options
fs_bar - Bar that shows how much space is used fs_bar - Bar that shows how much space is used
@@ -42,8 +68,8 @@ auto waybar::modules::Disk::update() -> void {
*/ */
if (err != 0 || stats.f_blocks == 0) { if (err != 0 || stats.f_blocks == 0) {
event_box_.hide(); spdlog::warn("Disk: statvfs failed for path '{}' (errno={})", path, errno);
return; continue;
} }
float specific_free, specific_used, specific_total, divisor; float specific_free, specific_used, specific_total, divisor;
@@ -58,35 +84,58 @@ auto waybar::modules::Disk::update() -> void {
auto total = pow_format(stats.f_blocks * stats.f_frsize, "B", true); auto total = pow_format(stats.f_blocks * stats.f_frsize, "B", true);
auto percentage_used = (stats.f_blocks - stats.f_bfree) * 100 / stats.f_blocks; auto percentage_used = (stats.f_blocks - stats.f_bfree) * 100 / stats.f_blocks;
auto format = format_; std::string disk_format = format_;
auto state = getState(percentage_used); auto state = getState(percentage_used);
if (!state.empty() && config_["format-" + state].isString()) { if (!state.empty() && config_["format-" + state].isString()) {
format = config_["format-" + state].asString(); disk_format = config_["format-" + state].asString();
} }
if (format.empty()) { if (!disk_format.empty()) {
event_box_.hide(); if (had_valid_disk) {
} else { label += separator_;
event_box_.show(); }
label_.set_markup(fmt::format(
fmt::runtime(format), stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free), label += fmt::format(
fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), fmt::arg("used", used), fmt::runtime(disk_format), stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free),
fmt::arg("percentage_used", percentage_used), fmt::arg("total", total), fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks),
fmt::arg("path", path_), fmt::arg("specific_free", specific_free), fmt::arg("used", used), fmt::arg("percentage_used", percentage_used),
fmt::arg("specific_used", specific_used), fmt::arg("specific_total", specific_total))); fmt::arg("total", total), fmt::arg("path", path),
fmt::arg("specific_free", specific_free), fmt::arg("specific_used", specific_used),
fmt::arg("specific_total", specific_total));
} }
if (tooltipEnabled()) {
std::string tooltip_format = "{used} used out of {total} on {path} ({percentage_used}%)"; std::string tooltip_format = "{used} used out of {total} on {path} ({percentage_used}%)";
if (config_["tooltip-format"].isString()) { if (config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString(); tooltip_format = config_["tooltip-format"].asString();
} }
label_.set_tooltip_markup(fmt::format(
fmt::runtime(tooltip_format), stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free), if (!tooltip_format.empty()) {
fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), fmt::arg("used", used), if (had_valid_disk) {
fmt::arg("percentage_used", percentage_used), fmt::arg("total", total), tooltip_label += "\n";
fmt::arg("path", path_), fmt::arg("specific_free", specific_free), }
fmt::arg("specific_used", specific_used), fmt::arg("specific_total", specific_total)));
tooltip_label += fmt::format(
fmt::runtime(tooltip_format), stats.f_bavail * 100 / stats.f_blocks,
fmt::arg("free", free),
fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks),
fmt::arg("used", used), fmt::arg("percentage_used", percentage_used),
fmt::arg("total", total), fmt::arg("path", path),
fmt::arg("specific_free", specific_free), fmt::arg("specific_used", specific_used),
fmt::arg("specific_total", specific_total));
}
had_valid_disk = true;
}
if (had_valid_disk) {
event_box_.show();
} else {
event_box_.hide();
}
label_.set_markup(label);
if (tooltipEnabled() && !tooltip_label.empty()) {
label_.set_tooltip_markup(tooltip_label);
} }
// Call parent update // Call parent update
ALabel::update(); ALabel::update();
@@ -109,7 +158,7 @@ float waybar::modules::Disk::calc_specific_divisor(const std::string& divisor) {
return 1000.0 * 1000.0 * 1000.0 * 1000.0; return 1000.0 * 1000.0 * 1000.0 * 1000.0;
} else if (divisor == "TiB") { } else if (divisor == "TiB") {
return 1024.0 * 1024.0 * 1024.0 * 1024.0; return 1024.0 * 1024.0 * 1024.0 * 1024.0;
} else { // default to Bytes if it is anything that we don't recongnise } else { // default to Bytes if it is anything that we don't recognise
return 1.0; return 1.0;
} }
} }