Merge remote-tracking branch 'origin/master' into refactor/generic-tooltip

# Conflicts:
#	src/modules/disk.cpp
This commit is contained in:
Alex
2026-07-03 23:56:44 +02:00
14 changed files with 341 additions and 103 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);
+1
View File
@@ -30,6 +30,7 @@ class Tags : public waybar::AModule {
const waybar::Bar& bar_; const waybar::Bar& bar_;
Gtk::Box box_; Gtk::Box box_;
std::vector<Gtk::Button> buttons_; std::vector<Gtk::Button> buttons_;
bool hide_vacant_;
struct zdwl_ipc_output_v2* output_status_; struct zdwl_ipc_output_v2* output_status_;
}; };
+3
View File
@@ -38,6 +38,7 @@ class Mpris : public ALabel {
std::optional<std::string> artist; std::optional<std::string> artist;
std::optional<std::string> album; std::optional<std::string> album;
std::optional<std::string> album_artist;
std::optional<std::string> title; std::optional<std::string> title;
std::optional<std::string> length; // as HH:MM:SS std::optional<std::string> length; // as HH:MM:SS
std::optional<std::string> position; // same format std::optional<std::string> position; // same format
@@ -76,6 +77,8 @@ class Mpris : public ALabel {
std::string player_; std::string player_;
std::vector<std::string> ignored_players_; std::vector<std::string> ignored_players_;
bool prefer_album_artist_;
PlayerctlPlayerManager* manager; PlayerctlPlayerManager* manager;
PlayerctlPlayer* player; PlayerctlPlayer* player;
PlayerctlPlayer* last_active_player_ = nullptr; PlayerctlPlayer* last_active_player_ = nullptr;
+5 -1
View File
@@ -14,11 +14,14 @@ namespace waybar::modules::SNI {
class Host { class Host {
public: public:
Host(const std::size_t id, const Json::Value&, const Bar&, Host(const std::size_t id, const Json::Value&, const Bar&, const std::vector<std::string>&,
const std::function<void(std::unique_ptr<Item>&)>&, const std::function<void(std::unique_ptr<Item>&)>&,
const std::function<void(std::unique_ptr<Item>&)>&, const std::function<void()>&); const std::function<void(std::unique_ptr<Item>&)>&, const std::function<void()>&);
~Host(); ~Host();
void checkIgnoreList(const std::vector<std::string>& ignore_list,
const std::function<void(std::unique_ptr<Item>&)>& on_remove);
private: private:
void busAcquired(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring); void busAcquired(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring);
void nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring, void nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring,
@@ -47,6 +50,7 @@ class Host {
unsigned retry_count_ = 0; unsigned retry_count_ = 0;
const Json::Value& config_; const Json::Value& config_;
const Bar& bar_; const Bar& bar_;
const std::vector<std::string> ignore_list_;
const std::function<void(std::unique_ptr<Item>&)> on_add_; const std::function<void(std::unique_ptr<Item>&)> on_add_;
const std::function<void(std::unique_ptr<Item>&)> on_remove_; const std::function<void(std::unique_ptr<Item>&)> on_remove_;
const std::function<void()> on_update_; const std::function<void()> on_update_;
+3
View File
@@ -19,11 +19,14 @@ class Tray : public AModule {
private: private:
void onAdd(std::unique_ptr<Item>& item); void onAdd(std::unique_ptr<Item>& item);
void onRemove(std::unique_ptr<Item>& item); void onRemove(std::unique_ptr<Item>& item);
void checkIgnoreList(std::unique_ptr<Item>* item);
std::vector<std::string> parseIgnoreList(const Json::Value& config);
void queueUpdate(); void queueUpdate();
static inline std::size_t nb_hosts_ = 0; static inline std::size_t nb_hosts_ = 0;
Gtk::Box box_; Gtk::Box box_;
SNI::Watcher::singleton watcher_; SNI::Watcher::singleton watcher_;
std::vector<std::string> ignore_list_;
SNI::Host host_; SNI::Host host_;
}; };
+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
+5
View File
@@ -21,6 +21,11 @@ Addressed by *dwl/tags*
typeof: array ++ typeof: array ++
The label to display for each tag. The label to display for each tag.
*hide-vacant*: ++
typeof: bool ++
default: false ++
If set to true, tags without clients and that are not active will be hidden.
*disable-click*: ++ *disable-click*: ++
typeof: bool ++ typeof: bool ++
default: false ++ default: false ++
+10 -2
View File
@@ -36,9 +36,15 @@ auto waybar::modules::Backlight::update() -> void {
if (best->get_powered()) { if (best->get_powered()) {
event_box_.show(); event_box_.show();
const uint8_t percent = const uint8_t percent =
best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max()); best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max());
const uint8_t percent_exp =
best->get_max() == 0
? 100
: roundf(powf((float)best->get_actual() / best->get_max(), 1.0f / 2.718f) * 100);
// Get the state and apply state-specific format if available // Get the state and apply state-specific format if available
auto state = getState(percent); auto state = getState(percent);
std::string current_format = format_; std::string current_format = format_;
@@ -49,8 +55,10 @@ auto waybar::modules::Backlight::update() -> void {
} }
} }
std::string desc = fmt::format(fmt::runtime(current_format), fmt::arg("percent", percent), std::string desc =
fmt::arg("icon", getIcon(percent))); fmt::format(fmt::runtime(current_format), fmt::arg("percent", percent),
fmt::arg("percent_exp", percent_exp), fmt::arg("icon", getIcon(percent)),
fmt::arg("icon_exp", getIcon(percent_exp)));
label_.set_markup(desc); label_.set_markup(desc);
if (tooltipEnabled()) { if (tooltipEnabled()) {
std::string tooltip_format; std::string tooltip_format;
+80 -19
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,23 +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_;
}
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));
}
std::string tooltip_format = "{used} used out of {total} on {path} ({percentage_used}%)";
if (config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString();
}
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(); event_box_.show();
updateLabelAndTooltip( } else {
format, "{used} used out of {total} on {path} ({percentage_used}%)", event_box_.hide();
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), setLabelMarkup(label);
fmt::arg("path", path_), fmt::arg("specific_free", specific_free),
fmt::arg("specific_used", specific_used), fmt::arg("specific_total", specific_total)); if (tooltipEnabled() && !tooltip_label.empty()) {
setTooltipMarkup(tooltip_label);
} }
// Call parent update // Call parent update
ALabel::update(); ALabel::update();
@@ -97,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;
} }
} }
+12 -3
View File
@@ -70,7 +70,6 @@ static const zdwl_ipc_output_v2_listener output_status_listener_impl{
static void handle_global(void* data, struct wl_registry* registry, uint32_t name, static void handle_global(void* data, struct wl_registry* registry, uint32_t name,
const char* interface, uint32_t version) { const char* interface, uint32_t version) {
if (std::strcmp(interface, zdwl_ipc_manager_v2_interface.name) == 0) { if (std::strcmp(interface, zdwl_ipc_manager_v2_interface.name) == 0) {
auto* self = static_cast<Tags*>(data); auto* self = static_cast<Tags*>(data);
@@ -93,8 +92,8 @@ if (std::strcmp(interface, wl_seat_interface.name) == 0) {
version = std::min<uint32_t>(version, 1); version = std::min<uint32_t>(version, 1);
self->seat_ = static_cast<struct wl_seat*>( self->seat_ =
wl_registry_bind(registry, name, &wl_seat_interface, version)); static_cast<struct wl_seat*>(wl_registry_bind(registry, name, &wl_seat_interface, version));
} }
} }
static void handle_global_remove(void* data, struct wl_registry* registry, uint32_t name) { static void handle_global_remove(void* data, struct wl_registry* registry, uint32_t name) {
@@ -110,7 +109,11 @@ Tags::Tags(const std::string& id, const waybar::Bar& bar, const Json::Value& con
seat_{nullptr}, seat_{nullptr},
bar_(bar), bar_(bar),
box_{bar.orientation, 0}, box_{bar.orientation, 0},
hide_vacant_(false),
output_status_{nullptr} { output_status_{nullptr} {
if (config_["hide-vacant"].asBool()) {
hide_vacant_ = config_["hide-vacant"].asBool();
}
struct wl_display* display = Client::inst()->wl_display; struct wl_display* display = Client::inst()->wl_display;
struct wl_registry* registry = wl_display_get_registry(display); struct wl_registry* registry = wl_display_get_registry(display);
@@ -221,6 +224,12 @@ void Tags::handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint
} else { } else {
button.get_style_context()->remove_class("urgent"); button.get_style_context()->remove_class("urgent");
} }
if (hide_vacant_ && !clients && !(state & TAG_ACTIVE)) {
button.set_visible(false);
} else {
button.set_visible(true);
}
} }
void Tags::handle_active_output(zdwl_ipc_output_v2* zdwl_output_v2, uint32_t active) { void Tags::handle_active_output(zdwl_ipc_output_v2* zdwl_output_v2, uint32_t active) {
+13 -5
View File
@@ -1,5 +1,7 @@
#include "modules/image.hpp" #include "modules/image.hpp"
#include <config.hpp>
waybar::modules::Image::Image(const std::string& id, const Json::Value& config) waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
: AModule(config, "image", id), box_(Gtk::ORIENTATION_HORIZONTAL, 0) { : AModule(config, "image", id), box_(Gtk::ORIENTATION_HORIZONTAL, 0) {
box_.pack_start(image_); box_.pack_start(image_);
@@ -35,6 +37,13 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
size_ = 16; size_ = 16;
} }
if (config_["path"].isString()) {
auto result = Config::tryExpandPath(config_["path"].asString(), "");
path_ = result.empty() ? "" : result.front();
} else {
path_.clear();
}
delayWorker(); delayWorker();
} }
@@ -54,13 +63,12 @@ void waybar::modules::Image::refresh(int sig) {
} }
auto waybar::modules::Image::update() -> void { auto waybar::modules::Image::update() -> void {
if (config_["path"].isString()) { if (config_["exec"].isString()) {
path_ = config_["path"].asString();
} else if (config_["exec"].isString()) {
output_ = util::command::exec(config_["exec"].asString(), ""); output_ = util::command::exec(config_["exec"].asString(), "");
parseOutputRaw(); parseOutputRaw();
} else { // expand path if "~" or "$HOME" is present in original path
path_ = ""; auto result = Config::tryExpandPath(path_, "");
path_ = result.empty() ? "" : result.front();
} }
if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS)) { if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS)) {
+18
View File
@@ -31,6 +31,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
dynamic_separator_(" - "), dynamic_separator_(" - "),
truncate_hours_(true), truncate_hours_(true),
tooltip_len_limits_(false), tooltip_len_limits_(false),
prefer_album_artist_(false),
// this character is used in Gnome so it's fine to use it here // this character is used in Gnome so it's fine to use it here
ellipsis_("\u2026"), ellipsis_("\u2026"),
player_("playerctld"), player_("playerctld"),
@@ -68,6 +69,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
if (config_["enable-tooltip-len-limits"].isBool()) { if (config_["enable-tooltip-len-limits"].isBool()) {
tooltip_len_limits_ = config["enable-tooltip-len-limits"].asBool(); tooltip_len_limits_ = config["enable-tooltip-len-limits"].asBool();
} }
if (config_["prefer-album-artist"].isBool()) {
prefer_album_artist_ = config["prefer-album-artist"].asBool();
}
} }
if (config["artist-len"].isUInt()) { if (config["artist-len"].isUInt()) {
@@ -206,6 +210,12 @@ auto Mpris::getIconFromJson(const Json::Value& icons, const std::string& key) ->
auto Mpris::getArtistStr(const PlayerInfo& info, bool truncated) -> std::string { auto Mpris::getArtistStr(const PlayerInfo& info, bool truncated) -> std::string {
auto artist = info.artist.value_or(std::string()); auto artist = info.artist.value_or(std::string());
if (prefer_album_artist_) {
auto album_artist = info.album_artist.value_or(std::string());
if (!album_artist.empty()) {
artist = album_artist;
}
}
if (truncated && artist_len_ >= 0) waybar::util::utf8_truncate(artist, ellipsis_, artist_len_); if (truncated && artist_len_ >= 0) waybar::util::utf8_truncate(artist, ellipsis_, artist_len_);
return artist; return artist;
} }
@@ -521,6 +531,7 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
.status_string = player_status, .status_string = player_status,
.artist = std::nullopt, .artist = std::nullopt,
.album = std::nullopt, .album = std::nullopt,
.album_artist = std::nullopt,
.title = std::nullopt, .title = std::nullopt,
.length = std::nullopt, .length = std::nullopt,
}; };
@@ -532,6 +543,13 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
} }
if (error) goto errorexit; if (error) goto errorexit;
if (auto* album_artist_ =
playerctl_player_print_metadata_prop(player, "xesam:albumArtist", &error)) {
spdlog::debug("mpris[{}]: albumArtist = {}", info.name, album_artist_);
info.album_artist = album_artist_;
g_free(album_artist_);
}
if (auto* album_ = playerctl_player_get_album(last_active_player_, &error)) { if (auto* album_ = playerctl_player_get_album(last_active_player_, &error)) {
spdlog::debug("mpris[{}]: album = {}", info.name, album_); spdlog::debug("mpris[{}]: album = {}", info.name, album_);
info.album = album_; info.album = album_;
+57 -5
View File
@@ -10,6 +10,7 @@ static const unsigned RETRY_DELAY_MS = 200;
static const unsigned MAX_RETRIES = 10; static const unsigned MAX_RETRIES = 10;
Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar, Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar,
const std::vector<std::string>& ignore_list,
const std::function<void(std::unique_ptr<Item>&)>& on_add, const std::function<void(std::unique_ptr<Item>&)>& on_add,
const std::function<void(std::unique_ptr<Item>&)>& on_remove, const std::function<void(std::unique_ptr<Item>&)>& on_remove,
const std::function<void()>& on_update) const std::function<void()>& on_update)
@@ -20,6 +21,7 @@ Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar,
sigc::mem_fun(*this, &Host::busAcquired))), sigc::mem_fun(*this, &Host::busAcquired))),
config_(config), config_(config),
bar_(bar), bar_(bar),
ignore_list_(ignore_list),
on_add_(on_add), on_add_(on_add),
on_remove_(on_remove), on_remove_(on_remove),
on_update_(on_update) {} on_update_(on_update) {}
@@ -39,6 +41,42 @@ Host::~Host() {
g_clear_object(&watcher_); g_clear_object(&watcher_);
} }
void Host::checkIgnoreList(const std::vector<std::string>& ignore_list,
const std::function<void(std::unique_ptr<Item>&)>& on_remove) {
spdlog::debug("Host::checkIgnoreList - checking {} items against {} patterns", items_.size(),
ignore_list.size());
for (auto it = items_.begin(); it != items_.end();) {
auto& item = *it;
spdlog::debug(" Checking item: bus_name='{}', category='{}', icon_name='{}', title='{}'",
item->bus_name, item->category, item->icon_name, item->title);
bool should_remove = false;
for (const auto& ignored : ignore_list) {
if (item->bus_name.find(ignored) != std::string::npos ||
item->category.find(ignored) != std::string::npos ||
item->icon_name.find(ignored) != std::string::npos ||
item->id.find(ignored) != std::string::npos ||
item->title.find(ignored) != std::string::npos) {
spdlog::info(
"Host: Ignoring item bus_name='{}', category='{}', icon_name='{}', title='{}' - "
"matched pattern '{}'",
item->bus_name, item->category, item->icon_name, item->title, ignored);
on_remove(item);
should_remove = true;
break;
}
}
if (should_remove) {
it = items_.erase(it);
} else {
++it;
}
}
}
void Host::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name) { void Host::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name) {
watcher_id_ = Gio::DBus::watch_name(conn, "org.kde.StatusNotifierWatcher", watcher_id_ = Gio::DBus::watch_name(conn, "org.kde.StatusNotifierWatcher",
sigc::mem_fun(*this, &Host::nameAppeared), sigc::mem_fun(*this, &Host::nameAppeared),
@@ -82,7 +120,8 @@ void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
spdlog::error("Host: {}", error->message); spdlog::error("Host: {}", error->message);
g_clear_object(&host->cancellable_); g_clear_object(&host->cancellable_);
if (host->retry_count_ >= MAX_RETRIES) { if (host->retry_count_ >= MAX_RETRIES) {
spdlog::warn("Host: giving up on watcher proxy creation after {} retries", host->retry_count_); spdlog::warn("Host: giving up on watcher proxy creation after {} retries",
host->retry_count_);
return; return;
} }
host->retry_count_ += 1; host->retry_count_ += 1;
@@ -127,7 +166,9 @@ void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) {
g_signal_connect(host->watcher_, "item-unregistered", G_CALLBACK(&Host::itemUnregistered), data); g_signal_connect(host->watcher_, "item-unregistered", G_CALLBACK(&Host::itemUnregistered), data);
auto items = sn_watcher_dup_registered_items(host->watcher_); auto items = sn_watcher_dup_registered_items(host->watcher_);
if (items != nullptr) { if (items != nullptr) {
spdlog::info("Host: Found {} pre-registered SNI items", g_strv_length(items));
for (uint32_t i = 0; items[i] != nullptr; i += 1) { for (uint32_t i = 0; items[i] != nullptr; i += 1) {
spdlog::info("Host: Processing pre-registered item: {}", items[i]);
host->addRegisteredItem(items[i]); host->addRegisteredItem(items[i]);
} }
} }
@@ -136,7 +177,10 @@ void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) {
void Host::itemRegistered(SnWatcher* watcher, const gchar* service, gpointer data) { void Host::itemRegistered(SnWatcher* watcher, const gchar* service, gpointer data) {
auto host = static_cast<SNI::Host*>(data); auto host = static_cast<SNI::Host*>(data);
spdlog::info("Host::itemRegistered called with service: {}", service);
host->addRegisteredItem(service); host->addRegisteredItem(service);
// host->checkIgnoreList(host->ignore_list_, std::bind(&Host::itemUnregistered, host,
// std::placeholders::_1, std::placeholders::_2, data));
} }
void Host::itemUnregistered(SnWatcher* watcher, const gchar* service, gpointer data) { void Host::itemUnregistered(SnWatcher* watcher, const gchar* service, gpointer data) {
@@ -197,17 +241,25 @@ std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::st
} }
void Host::addRegisteredItem(const std::string& service) { void Host::addRegisteredItem(const std::string& service) {
// Check service string directly before parsing
for (const auto& ignored : ignore_list_) {
if (service.find(ignored) != std::string::npos) {
spdlog::info("Host: Ignoring service '{}' - matched pattern '{}'", service, ignored);
return;
}
}
std::string bus_name, object_path; std::string bus_name, object_path;
std::tie(bus_name, object_path) = getBusNameAndObjectPath(service); std::tie(bus_name, object_path) = getBusNameAndObjectPath(service);
spdlog::debug("SNI item registered: bus_name={}, object_path={}, full_service={}", bus_name,
object_path, service);
auto it = std::find_if(items_.begin(), items_.end(), [&bus_name, &object_path](const auto& item) { auto it = std::find_if(items_.begin(), items_.end(), [&bus_name, &object_path](const auto& item) {
return bus_name == item->bus_name && object_path == item->object_path; return bus_name == item->bus_name && object_path == item->object_path;
}); });
if (it == items_.end()) { if (it == items_.end()) {
spdlog::debug("Adding SNI item: {}", bus_name);
items_.emplace_back(std::make_unique<Item>( items_.emplace_back(std::make_unique<Item>(
bus_name, object_path, config_, bar_, bus_name, object_path, config_, bar_, [this](Item& item) { itemReady(item); },
[this](Item& item) { itemReady(item); }, [this](Item& item) { itemInvalidated(item); }, on_update_));
[this](Item& item) { itemInvalidated(item); },
on_update_));
} }
} }
+37 -1
View File
@@ -8,11 +8,29 @@
namespace waybar::modules::SNI { namespace waybar::modules::SNI {
std::vector<std::string> Tray::parseIgnoreList(const Json::Value& config) {
std::vector<std::string> ignore_list;
if (config["ignore-list"].isArray()) {
spdlog::info("Tray: Found ignore-list with {} items", config["ignore-list"].size());
for (const auto& item : config["ignore-list"]) {
if (item.isString()) {
ignore_list.push_back(item.asString());
spdlog::info("Tray: Adding to ignore list: {}", item.asString());
}
}
} else {
spdlog::info("Tray: No ignore-list configured");
}
return ignore_list;
}
Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config) Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
: AModule(config, "tray", id), : AModule(config, "tray", id),
box_(bar.orientation, 0), box_(bar.orientation, 0),
watcher_(SNI::Watcher::getInstance()), watcher_(SNI::Watcher::getInstance()),
host_(nb_hosts_, config, bar, std::bind(&Tray::onAdd, this, std::placeholders::_1), ignore_list_(parseIgnoreList(config)),
host_(nb_hosts_, config, bar, ignore_list_,
std::bind(&Tray::onAdd, this, std::placeholders::_1),
std::bind(&Tray::onRemove, this, std::placeholders::_1), std::bind(&Tray::onRemove, this, std::placeholders::_1),
std::bind(&Tray::queueUpdate, this)) { std::bind(&Tray::queueUpdate, this)) {
box_.set_name("tray"); box_.set_name("tray");
@@ -31,14 +49,26 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
dp.emit(); dp.emit();
} }
void Tray::checkIgnoreList(std::unique_ptr<Item>* item_ptr) {
// Delegate to Host's checkIgnoreList method
host_.checkIgnoreList(ignore_list_, std::bind(&Tray::onRemove, this, std::placeholders::_1));
}
void Tray::queueUpdate() { dp.emit(); } void Tray::queueUpdate() { dp.emit(); }
void Tray::onAdd(std::unique_ptr<Item>& item) { void Tray::onAdd(std::unique_ptr<Item>& item) {
spdlog::info("Tray::onAdd - item bus_name='{}', category='{}', icon_name='{}', title='{}'",
item->bus_name, item->category, item->icon_name, item->title);
if (config_["reverse-direction"].isBool() && config_["reverse-direction"].asBool()) { if (config_["reverse-direction"].isBool() && config_["reverse-direction"].asBool()) {
box_.pack_end(item->event_box); box_.pack_end(item->event_box);
} else { } else {
box_.pack_start(item->event_box); box_.pack_start(item->event_box);
} }
spdlog::debug("Tray::onAdd deferred check - checking ignore list");
host_.checkIgnoreList(ignore_list_, std::bind(&Tray::onRemove, this, std::placeholders::_1));
item->event_box.signal_show().connect([this] { dp.emit(); }); item->event_box.signal_show().connect([this] { dp.emit(); });
item->event_box.signal_hide().connect([this] { dp.emit(); }); item->event_box.signal_hide().connect([this] { dp.emit(); });
dp.emit(); dp.emit();
@@ -50,6 +80,12 @@ void Tray::onRemove(std::unique_ptr<Item>& item) {
} }
auto Tray::update() -> void { auto Tray::update() -> void {
// Check if any items should be ignored now that properties have loaded
if (!ignore_list_.empty()) {
spdlog::debug("Tray::update() - checking ignore list");
host_.checkIgnoreList(ignore_list_, std::bind(&Tray::onRemove, this, std::placeholders::_1));
}
std::vector<Gtk::Widget*> children = box_.get_children(); std::vector<Gtk::Widget*> children = box_.get_children();
event_box_.set_visible(std::any_of(children.begin(), children.end(), event_box_.set_visible(std::any_of(children.begin(), children.end(),
[](Gtk::Widget* child) { return child->get_visible(); })); [](Gtk::Widget* child) { return child->get_visible(); }));