Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion include/modules/disk.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <sys/statvfs.h>

#include <fstream>
#include <vector>

#include "ALabel.hpp"
#include "util/format.hpp"
Expand All @@ -19,7 +20,9 @@ class Disk : public ALabel {

private:
util::SleeperThread thread_;
std::string path_;
std::string header_;
std::vector<std::string> paths_;
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The header file uses std::vector but does not include the vector header. While this may compile due to transitive includes, it's fragile and violates best practices. Add #include <vector> to the includes section.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

std::string separator_;
std::string unit_;

float calc_specific_divisor(const std::string divisor);
Expand Down
45 changes: 36 additions & 9 deletions man/waybar-disk.5.scd
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,12 @@ waybar - disk module

# DESCRIPTION

The *disk* module displays the current disk space used.
The *disk* module displays information of multiple disks.

# CONFIGURATION

Addressed by *disk*

*path*: ++
typeof: string ++
default: "/" ++
Any path residing in the filesystem or mountpoint for which the information should be displayed.

*interval*: ++
typeof: integer++
default: 30 ++
Expand All @@ -25,7 +20,7 @@ Addressed by *disk*
*format*: ++
typeof: string ++
default: "{percentage_used}%" ++
The format, how information should be displayed.
The format, how information for each disk should be displayed.

*rotate*: ++
typeof: integer ++
Expand Down Expand Up @@ -75,6 +70,26 @@ Addressed by *disk*
typeof: string ++
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.

Comment on lines 78 to 92
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deprecated "path" configuration option is not documented. Since the PR description states this is a breaking change with backward compatibility and existing configurations using "path" will continue to work with a deprecation warning, the documentation should mention this deprecated option and indicate that users should migrate to "paths".

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
Expand Down Expand Up @@ -123,7 +138,7 @@ Addressed by *disk*

*{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.

Expand All @@ -143,10 +158,22 @@ Addressed by *disk*
```
"disk": {
"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",
"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
Expand Down
179 changes: 114 additions & 65 deletions src/modules/disk.cpp
Original file line number Diff line number Diff line change
@@ -1,92 +1,141 @@
#include "modules/disk.hpp"

#include <spdlog/spdlog.h>

using namespace waybar::util;

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] {
dp.emit();
thread_.sleep_for(interval_);
};
if (config["path"].isString()) {
path_ = config["path"].asString();
if (config["header"].isString()) {
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("/");
}
Comment on lines 16 to 27
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When only the deprecated "path" config is provided (not "paths" array), both the configured path and the default "/" are added to paths_. This happens because line 16-18 adds the configured path, but then the else block at lines 24-25 also executes (since config["paths"] is not an array), adding "/" as a second entry. The else block should only add "/" when neither "path" nor "paths" is configured.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if (config["separator"].isString()) {
separator_ = config["separator"].asString();
}
if (config["unit"].isString()) {
unit_ = config["unit"].asString();
}
}

auto waybar::modules::Disk::update() -> void {
struct statvfs /* {
unsigned long f_bsize; // filesystem block size
unsigned long f_frsize; // fragment size
fsblkcnt_t f_blocks; // size of fs in f_frsize units
fsblkcnt_t f_bfree; // # free blocks
fsblkcnt_t f_bavail; // # free blocks for unprivileged users
fsfilcnt_t f_files; // # inodes
fsfilcnt_t f_ffree; // # free inodes
fsfilcnt_t f_favail; // # free inodes for unprivileged users
unsigned long f_fsid; // filesystem ID
unsigned long f_flag; // mount flags
unsigned long f_namemax; // maximum filename length
}; */
stats;
int err = statvfs(path_.c_str(), &stats);

/* Conky options
fs_bar - Bar that shows how much space is used
fs_free - Free space on a file system
fs_free_perc - Free percentage of space
fs_size - File system size
fs_used - File system used space
*/

if (err != 0) {
event_box_.hide();
return;
}
std::string tooltip_label;
std::string label = header_;

float specific_free, specific_used, specific_total, divisor;
bool had_valid_disk = false;

divisor = calc_specific_divisor(unit_);
specific_free = (stats.f_bavail * stats.f_frsize) / divisor;
specific_used = ((stats.f_blocks - stats.f_bfree) * stats.f_frsize) / divisor;
specific_total = (stats.f_blocks * stats.f_frsize) / divisor;
for (size_t i = 0; i < paths_.size(); ++i) {
const auto& path = paths_[i];

auto free = pow_format(stats.f_bavail * stats.f_frsize, "B", true);
auto used = pow_format((stats.f_blocks - stats.f_bfree) * 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;
struct statvfs /* {
unsigned long f_bsize; // filesystem block size
unsigned long f_frsize; // fragment size
fsblkcnt_t f_blocks; // size of fs in f_frsize units
fsblkcnt_t f_bfree; // # free blocks
fsblkcnt_t f_bavail; // # free blocks for unprivileged users
fsfilcnt_t f_files; // # inodes
fsfilcnt_t f_ffree; // # free inodes
fsfilcnt_t f_favail; // # free inodes for unprivileged users
unsigned long f_fsid; // filesystem ID
unsigned long f_flag; // mount flags
unsigned long f_namemax; // maximum filename length
}; */
stats;

auto format = format_;
auto state = getState(percentage_used);
if (!state.empty() && config_["format-" + state].isString()) {
format = config_["format-" + state].asString();
}
int err = statvfs(path.c_str(), &stats);

if (format.empty()) {
event_box_.hide();
} else {
event_box_.show();
label_.set_markup(fmt::format(
fmt::runtime(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)));
}
/* Conky options
fs_bar - Bar that shows how much space is used
fs_free - Free space on a file system
fs_free_perc - Free percentage of space
fs_size - File system size
fs_used - File system used space
*/

if (err != 0) {
spdlog::warn("Disk: statvfs failed for path '{}' (errno={})", path, errno);
continue;
}

float specific_free, specific_used, specific_total, divisor;

divisor = calc_specific_divisor(unit_);
specific_free = (stats.f_bavail * stats.f_frsize) / divisor;
specific_used = ((stats.f_blocks - stats.f_bfree) * stats.f_frsize) / divisor;
specific_total = (stats.f_blocks * stats.f_frsize) / divisor;

auto free = pow_format(stats.f_bavail * stats.f_frsize, "B", true);
auto used = pow_format((stats.f_blocks - stats.f_bfree) * 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;

std::string disk_format = format_;
auto state = getState(percentage_used);
if (!state.empty() && config_["format-" + state].isString()) {
disk_format = config_["format-" + state].asString();
}

if (!disk_format.empty()) {
if (had_valid_disk) {
label += separator_;
}

label += fmt::format(
fmt::runtime(disk_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));
}

if (tooltipEnabled()) {
std::string tooltip_format = "{used} used out of {total} on {path} ({percentage_used}%)";
if (config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString();
}
label_.set_tooltip_text(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)));

if (!tooltip_format.empty()) {
if (had_valid_disk) {
tooltip_label += "\n";
}

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_text(tooltip_label);
}
// Call parent update
ALabel::update();
Expand All @@ -109,7 +158,7 @@ float waybar::modules::Disk::calc_specific_divisor(std::string divisor) {
return 1000.0 * 1000.0 * 1000.0 * 1000.0;
} else if (divisor == "TiB") {
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;
}
}
}