From 0fdc4c11f3eab5b97c42116f69ac7656d8d3e194 Mon Sep 17 00:00:00 2001 From: Narice Date: Tue, 6 Dec 2022 18:40:38 +0100 Subject: [PATCH 1/8] feat(backlight): added percentexp as format option Humans don't perceive light linearly and so an exponential curve is more appropriate to gauge the perceived brightness of the screen. --- src/modules/backlight.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index aa734a4f..bf3ae226 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -179,9 +179,17 @@ auto waybar::modules::Backlight::update() -> void { if (best->get_powered()) { event_box_.show(); + const uint8_t percent = 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); + label_.set_markup(fmt::format(format_, fmt::arg("percent", std::to_string(percent)), + fmt::arg("percentexp", std::to_string(percent_exp)), fmt::arg("icon", getIcon(percent)))); getState(percent); } else { From dbb8a881be2b44845a3dbff8cb3e10a66ecbc902 Mon Sep 17 00:00:00 2001 From: Narice Date: Tue, 6 Dec 2022 19:22:21 +0100 Subject: [PATCH 2/8] feat(backlight): Added icon_exp and changed syntax changed syntax of percentexp to percent_exp for readability --- src/modules/backlight.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modules/backlight.cpp b/src/modules/backlight.cpp index bf3ae226..2df0dc1f 100644 --- a/src/modules/backlight.cpp +++ b/src/modules/backlight.cpp @@ -189,8 +189,9 @@ auto waybar::modules::Backlight::update() -> void { : roundf(powf((float)best->get_actual() / best->get_max(), 1.0f / 2.718f) * 100); label_.set_markup(fmt::format(format_, fmt::arg("percent", std::to_string(percent)), - fmt::arg("percentexp", std::to_string(percent_exp)), - fmt::arg("icon", getIcon(percent)))); + fmt::arg("percent_exp", std::to_string(percent_exp)), + fmt::arg("icon", getIcon(percent)), + fmt::arg("icon_exp", getIcon(percent_exp)))); getState(percent); } else { event_box_.hide(); From 807208cba8c52b56c5c8430a4fc7c88d9666c098 Mon Sep 17 00:00:00 2001 From: Alex Denes Date: Sat, 8 Feb 2025 03:53:28 +0000 Subject: [PATCH 3/8] dwl/tags: add hide-vacant option --- include/modules/dwl/tags.hpp | 1 + man/waybar-dwl-tags.5.scd | 5 +++++ src/modules/dwl/tags.cpp | 10 ++++++++++ 3 files changed, 16 insertions(+) diff --git a/include/modules/dwl/tags.hpp b/include/modules/dwl/tags.hpp index 53dff989..8f2a158a 100644 --- a/include/modules/dwl/tags.hpp +++ b/include/modules/dwl/tags.hpp @@ -28,6 +28,7 @@ class Tags : public waybar::AModule { const waybar::Bar &bar_; Gtk::Box box_; std::vector buttons_; + bool hide_vacant_; struct zdwl_ipc_output_v2 *output_status_; }; diff --git a/man/waybar-dwl-tags.5.scd b/man/waybar-dwl-tags.5.scd index a2146dfd..d81cc546 100644 --- a/man/waybar-dwl-tags.5.scd +++ b/man/waybar-dwl-tags.5.scd @@ -21,6 +21,11 @@ Addressed by *dwl/tags* typeof: array ++ 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*: ++ typeof: bool ++ default: false ++ diff --git a/src/modules/dwl/tags.cpp b/src/modules/dwl/tags.cpp index f8b250c8..ce4c2324 100644 --- a/src/modules/dwl/tags.cpp +++ b/src/modules/dwl/tags.cpp @@ -94,7 +94,11 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con seat_{nullptr}, bar_(bar), box_{bar.orientation, 0}, + hide_vacant_(false), output_status_{nullptr} { + if (config_["hide-vacant"].asBool()) { + hide_vacant_ = config_["hide-vacant"].asBool(); + } struct wl_display *display = Client::inst()->wl_display; struct wl_registry *registry = wl_display_get_registry(display); @@ -198,6 +202,12 @@ void Tags::handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint } else { button.get_style_context()->remove_class("urgent"); } + + if (hide_vacant_ && !clients && !(state & TAG_ACTIVE)) { + button.set_visible(false); + } else { + button.set_visible(true); + } } } /* namespace waybar::modules::dwl */ From 9b7a8a8c21a43f94a421a0db65244e8277b4a603 Mon Sep 17 00:00:00 2001 From: hecate cantus Date: Sun, 27 Apr 2025 20:30:14 -0700 Subject: [PATCH 4/8] implement albumArtist todo testing --- include/modules/mpris/mpris.hpp | 3 +++ src/modules/mpris/mpris.cpp | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/modules/mpris/mpris.hpp b/include/modules/mpris/mpris.hpp index ad4dac1e..a07a6358 100644 --- a/include/modules/mpris/mpris.hpp +++ b/include/modules/mpris/mpris.hpp @@ -38,6 +38,7 @@ class Mpris : public ALabel { std::optional artist; std::optional album; + std::optional album_artist; std::optional title; std::optional length; // as HH:MM:SS std::optional position; // same format @@ -76,6 +77,8 @@ class Mpris : public ALabel { std::string player_; std::vector ignored_players_; + bool prefer_album_artist_; + PlayerctlPlayerManager* manager; PlayerctlPlayer* player; std::string lastStatus; diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index ed383b0c..2492f3b5 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -31,6 +31,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) dynamic_separator_(" - "), truncate_hours_(true), tooltip_len_limits_(false), + prefer_album_artist_(false), // this character is used in Gnome so it's fine to use it here ellipsis_("\u2026"), player_("playerctld"), @@ -68,6 +69,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) if (config_["enable-tooltip-len-limits"].isBool()) { 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()) { @@ -244,6 +248,12 @@ void truncate(std::string& s, const std::string& ellipsis, size_t max_len) { auto Mpris::getArtistStr(const PlayerInfo& info, bool truncated) -> 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) truncate(artist, ellipsis_, artist_len_); return artist; } @@ -516,6 +526,7 @@ auto Mpris::getPlayerInfo() -> std::optional { .status_string = player_status, .artist = std::nullopt, .album = std::nullopt, + .album_artist = std::nullopt, .title = std::nullopt, .length = std::nullopt, }; @@ -527,6 +538,12 @@ auto Mpris::getPlayerInfo() -> std::optional { } 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(player, &error)) { spdlog::debug("mpris[{}]: album = {}", info.name, album_); info.album = album_; From 16d7c9d8497465a360e214f40944a2624852ea30 Mon Sep 17 00:00:00 2001 From: zspher <66728045+zspher@users.noreply.github.com> Date: Tue, 28 Oct 2025 13:35:48 -0600 Subject: [PATCH 5/8] feat(image): expand `~` & `$HOME` in path --- src/modules/image.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 173aabd3..8b9888bc 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -1,5 +1,7 @@ #include "modules/image.hpp" +#include + waybar::modules::Image::Image(const std::string& id, const Json::Value& config) : AModule(config, "image", id), box_(Gtk::ORIENTATION_HORIZONTAL, 0) { box_.pack_start(image_); @@ -56,6 +58,10 @@ auto waybar::modules::Image::update() -> void { path_ = ""; } + // expand path if "~" or "$HOME" is present in original path + auto result = Config::tryExpandPath(path_, ""); + path_ = result.empty() ? "" : result.front(); + if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS)) { Glib::RefPtr pixbuf; From d61bc859ae7a086abed0f9b93cf69ce7f4e8b3bf Mon Sep 17 00:00:00 2001 From: zspher <66728045+zspher@users.noreply.github.com> Date: Wed, 29 Oct 2025 17:07:36 -0600 Subject: [PATCH 6/8] perf(image): set image from `path` only once --- src/modules/image.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 8b9888bc..02b6d336 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -28,6 +28,13 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config) size_ = 16; } + if (config_["path"].isString()) { + auto result = Config::tryExpandPath(config_["path"].asString(), ""); + path_ = result.empty() ? "" : result.front(); + } else { + path_.clear(); + } + if (interval_.count() == 0) { interval_ = std::chrono::milliseconds::max(); } @@ -49,19 +56,14 @@ void waybar::modules::Image::refresh(int sig) { } auto waybar::modules::Image::update() -> void { - if (config_["path"].isString()) { - path_ = config_["path"].asString(); - } else if (config_["exec"].isString()) { + if (config_["exec"].isString()) { output_ = util::command::exec(config_["exec"].asString(), ""); parseOutputRaw(); - } else { - path_ = ""; + // expand path if "~" or "$HOME" is present in original path + auto result = Config::tryExpandPath(path_, ""); + path_ = result.empty() ? "" : result.front(); } - // expand path if "~" or "$HOME" is present in original path - auto result = Config::tryExpandPath(path_, ""); - path_ = result.empty() ? "" : result.front(); - if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS)) { Glib::RefPtr pixbuf; From ca0b068ad86ed3b6ba949a699fbfe676d1d3f320 Mon Sep 17 00:00:00 2001 From: szbmrk Date: Fri, 9 Jan 2026 17:59:11 +0100 Subject: [PATCH 7/8] feat(disk)!: handle multiple disks at once --- include/modules/disk.hpp | 5 +- man/waybar-disk.5.scd | 45 ++++++++-- src/modules/disk.cpp | 175 +++++++++++++++++++++++++-------------- 3 files changed, 152 insertions(+), 73 deletions(-) diff --git a/include/modules/disk.hpp b/include/modules/disk.hpp index 1b4f3176..ea6f89c8 100644 --- a/include/modules/disk.hpp +++ b/include/modules/disk.hpp @@ -4,6 +4,7 @@ #include #include +#include #include "ALabel.hpp" #include "util/format.hpp" @@ -19,7 +20,9 @@ class Disk : public ALabel { private: util::SleeperThread thread_; - std::string path_; + std::string header_; + std::vector paths_; + std::string separator_; std::string unit_; float calc_specific_divisor(const std::string divisor); diff --git a/man/waybar-disk.5.scd b/man/waybar-disk.5.scd index 00af2b90..dab6af7e 100644 --- a/man/waybar-disk.5.scd +++ b/man/waybar-disk.5.scd @@ -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 ++ @@ -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 ++ @@ -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. + *smooth-scrolling-threshold*: ++ typeof: double ++ 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. -*{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. @@ -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 diff --git a/src/modules/disk.cpp b/src/modules/disk.cpp index ef257b72..695ae157 100644 --- a/src/modules/disk.cpp +++ b/src/modules/disk.cpp @@ -1,15 +1,32 @@ #include "modules/disk.hpp" +#include + 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("/"); + } + if (config["separator"].isString()) { + separator_ = config["separator"].asString(); } if (config["unit"].isString()) { unit_ = config["unit"].asString(); @@ -17,76 +34,108 @@ waybar::modules::Disk::Disk(const std::string& id, const Json::Value& config) } 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); + std::string tooltip_label; + std::string label = header_; - /* 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 - */ + bool had_valid_disk = false; - if (err != 0) { - event_box_.hide(); - return; - } + for (size_t i = 0; i < paths_.size(); ++i) { + const auto& path = paths_[i]; - float specific_free, specific_used, specific_total, divisor; + 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; - 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; + int err = statvfs(path.c_str(), &stats); - 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; + /* 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 + */ - auto format = format_; - auto state = getState(percentage_used); - if (!state.empty() && config_["format-" + state].isString()) { - format = config_["format-" + state].asString(); - } + if (err != 0) { + spdlog::warn("Disk: statvfs failed for path '{}' (errno={})", path, errno); + continue; + } - 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))); - } + 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(); @@ -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; } -} \ No newline at end of file +} From 3655d735254411b44031cddc9eb460f4e923220f Mon Sep 17 00:00:00 2001 From: anton <1711399+AntGarSil@users.noreply.github.com> Date: Sun, 8 Feb 2026 00:44:14 +0000 Subject: [PATCH 8/8] Ignore Tray entries by service name and when dbus properties load --- include/modules/sni/host.hpp | 5 ++++ include/modules/sni/tray.hpp | 3 +++ src/modules/sni/host.cpp | 48 ++++++++++++++++++++++++++++++++++++ src/modules/sni/tray.cpp | 38 +++++++++++++++++++++++++++- 4 files changed, 93 insertions(+), 1 deletion(-) diff --git a/include/modules/sni/host.hpp b/include/modules/sni/host.hpp index 6c62ac31..5b475708 100644 --- a/include/modules/sni/host.hpp +++ b/include/modules/sni/host.hpp @@ -15,9 +15,13 @@ namespace waybar::modules::SNI { class Host { public: Host(const std::size_t id, const Json::Value&, const Bar&, + const std::vector&, const std::function&)>&, const std::function&)>&); ~Host(); + + void checkIgnoreList(const std::vector& ignore_list, + const std::function&)>& on_remove); private: void busAcquired(const Glib::RefPtr&, Glib::ustring); @@ -41,6 +45,7 @@ class Host { SnWatcher* watcher_ = nullptr; const Json::Value& config_; const Bar& bar_; + const std::vector ignore_list_; const std::function&)> on_add_; const std::function&)> on_remove_; }; diff --git a/include/modules/sni/tray.hpp b/include/modules/sni/tray.hpp index 5f12d7f2..9288ec9a 100644 --- a/include/modules/sni/tray.hpp +++ b/include/modules/sni/tray.hpp @@ -19,11 +19,14 @@ class Tray : public AModule { private: void onAdd(std::unique_ptr& item); void onRemove(std::unique_ptr& item); + void checkIgnoreList(std::unique_ptr* item); + std::vector parseIgnoreList(const Json::Value& config); static inline std::size_t nb_hosts_ = 0; bool show_passive_ = false; Gtk::Box box_; SNI::Watcher::singleton watcher_; + std::vector ignore_list_; SNI::Host host_; }; diff --git a/src/modules/sni/host.cpp b/src/modules/sni/host.cpp index 54faa16c..e9893465 100644 --- a/src/modules/sni/host.cpp +++ b/src/modules/sni/host.cpp @@ -7,6 +7,7 @@ namespace waybar::modules::SNI { Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar, + const std::vector& ignore_list, const std::function&)>& on_add, const std::function&)>& on_remove) : bus_name_("org.kde.StatusNotifierHost-" + std::to_string(getpid()) + "-" + @@ -16,6 +17,7 @@ Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar, sigc::mem_fun(*this, &Host::busAcquired))), config_(config), bar_(bar), + ignore_list_(ignore_list), on_add_(on_add), on_remove_(on_remove) {} @@ -33,6 +35,39 @@ Host::~Host() { g_clear_object(&watcher_); } +void Host::checkIgnoreList(const std::vector& ignore_list, + const std::function&)>& 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& conn, Glib::ustring name) { watcher_id_ = Gio::DBus::watch_name(conn, "org.kde.StatusNotifierWatcher", sigc::mem_fun(*this, &Host::nameAppeared), @@ -100,7 +135,9 @@ void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) { g_signal_connect(host->watcher_, "item-unregistered", G_CALLBACK(&Host::itemUnregistered), data); auto items = sn_watcher_dup_registered_items(host->watcher_); 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) { + spdlog::info("Host: Processing pre-registered item: {}", items[i]); host->addRegisteredItem(items[i]); } } @@ -109,7 +146,9 @@ void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) { void Host::itemRegistered(SnWatcher* watcher, const gchar* service, gpointer data) { auto host = static_cast(data); + spdlog::info("Host::itemRegistered called with service: {}", 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) { @@ -133,12 +172,21 @@ std::tuple Host::getBusNameAndObjectPath(const std::st } void Host::addRegisteredItem(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::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) { return bus_name == item->bus_name && object_path == item->object_path; }); if (it == items_.end()) { + spdlog::debug("Adding SNI item: {}", bus_name); items_.emplace_back(new Item(bus_name, object_path, config_, bar_)); on_add_(items_.back()); } diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index 34a3c05f..248ddf8e 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -8,11 +8,29 @@ namespace waybar::modules::SNI { +std::vector Tray::parseIgnoreList(const Json::Value& config) { + std::vector 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) : AModule(config, "tray", id), box_(bar.orientation, 0), 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)) { box_.set_name("tray"); event_box_.add(box_); @@ -33,12 +51,24 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config) dp.emit(); } +void Tray::checkIgnoreList(std::unique_ptr* item_ptr) { + // Delegate to Host's checkIgnoreList method + host_.checkIgnoreList(ignore_list_, std::bind(&Tray::onRemove, this, std::placeholders::_1)); +} + void Tray::onAdd(std::unique_ptr& 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()) { box_.pack_end(item->event_box); } else { 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)); + dp.emit(); } @@ -48,6 +78,12 @@ void Tray::onRemove(std::unique_ptr& item) { } 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)); + } + // Show tray only when items are available std::vector children = box_.get_children(); if (show_passive_) {