Merge remote-tracking branch 'origin/master' into pr-4624

# Conflicts:
#	include/modules/sni/item.hpp
#	src/modules/sni/item.cpp
#	src/modules/sni/tray.cpp
This commit is contained in:
Alex
2026-07-04 00:39:23 +02:00
272 changed files with 11591 additions and 3261 deletions
+130 -11
View File
@@ -6,9 +6,14 @@
namespace waybar::modules::SNI {
static const unsigned RETRY_DELAY_MS = 200;
static const unsigned MAX_RETRIES = 10;
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_remove)
const std::function<void(std::unique_ptr<Item>&)>& on_remove,
const std::function<void()>& on_update)
: bus_name_("org.kde.StatusNotifierHost-" + std::to_string(getpid()) + "-" +
std::to_string(id)),
object_path_("/StatusNotifierHost/" + std::to_string(id)),
@@ -16,10 +21,13 @@ 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) {}
on_remove_(on_remove),
on_update_(on_update) {}
Host::~Host() {
retry_connection_.disconnect();
if (bus_name_id_ > 0) {
Gio::DBus::unown_name(bus_name_id_);
bus_name_id_ = 0;
@@ -33,6 +41,42 @@ Host::~Host() {
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) {
watcher_id_ = Gio::DBus::watch_name(conn, "org.kde.StatusNotifierWatcher",
sigc::mem_fun(*this, &Host::nameAppeared),
@@ -51,15 +95,17 @@ void Host::nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>& conn, const G
}
void Host::nameVanished(const Glib::RefPtr<Gio::DBus::Connection>& conn, const Glib::ustring name) {
retry_connection_.disconnect();
retry_count_ = 0;
g_cancellable_cancel(cancellable_);
g_clear_object(&cancellable_);
g_clear_object(&watcher_);
items_.clear();
clearItems();
}
void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() {
waybar::util::ScopeGuard error_deleter([&error]() {
if (error != nullptr) {
g_error_free(error);
}
@@ -70,18 +116,38 @@ void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
return;
}
auto host = static_cast<SNI::Host*>(data);
host->watcher_ = watcher;
if (error != nullptr) {
spdlog::error("Host: {}", error->message);
g_clear_object(&host->cancellable_);
if (host->retry_count_ >= MAX_RETRIES) {
spdlog::warn("Host: giving up on watcher proxy creation after {} retries",
host->retry_count_);
return;
}
host->retry_count_ += 1;
// Store the timeout connection so it is disconnected in ~Host, avoiding a
// use-after-free if the Host is destroyed before the retry fires.
host->retry_connection_ = Glib::signal_timeout().connect(
[host]() {
if (host->watcher_ != nullptr) {
return false;
}
auto conn = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::BUS_TYPE_SESSION);
host->nameAppeared(conn, "org.kde.StatusNotifierWatcher", "");
return false;
},
RETRY_DELAY_MS);
return;
}
host->retry_count_ = 0;
host->watcher_ = watcher;
sn_watcher_call_register_host(host->watcher_, host->object_path_.c_str(), host->cancellable_,
&Host::registerHost, data);
}
void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) {
GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() {
waybar::util::ScopeGuard error_deleter([&error]() {
if (error != nullptr) {
g_error_free(error);
}
@@ -100,7 +166,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 +177,10 @@ void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) {
void Host::itemRegistered(SnWatcher* watcher, const gchar* service, gpointer data) {
auto host = static_cast<SNI::Host*>(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) {
@@ -117,13 +188,50 @@ void Host::itemUnregistered(SnWatcher* watcher, const gchar* service, gpointer d
auto [bus_name, object_path] = host->getBusNameAndObjectPath(service);
for (auto it = host->items_.begin(); it != host->items_.end(); ++it) {
if ((*it)->bus_name == bus_name && (*it)->object_path == object_path) {
host->on_remove_(*it);
host->items_.erase(it);
host->removeItem(it);
break;
}
}
}
void Host::itemReady(Item& item) {
auto it = std::find_if(items_.begin(), items_.end(),
[&item](const auto& candidate) { return candidate.get() == &item; });
if (it != items_.end() && (*it)->isReady()) {
on_add_(*it);
}
}
void Host::itemInvalidated(Item& item) {
auto it = std::find_if(items_.begin(), items_.end(),
[&item](const auto& candidate) { return candidate.get() == &item; });
if (it != items_.end()) {
removeItem(it);
}
}
void Host::removeItem(std::vector<std::unique_ptr<Item>>::iterator it) {
if ((*it)->isReady()) {
on_remove_(*it);
}
items_.erase(it);
}
void Host::clearItems() {
bool removed_ready_item = false;
for (auto& item : items_) {
if (item->isReady()) {
on_remove_(item);
removed_ready_item = true;
}
}
bool had_items = !items_.empty();
items_.clear();
if (had_items && !removed_ready_item) {
on_update_();
}
}
std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::string service) {
auto it = service.find('/');
if (it != std::string::npos) {
@@ -132,15 +240,26 @@ std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::st
return {service, "/StatusNotifierItem"};
}
void Host::addRegisteredItem(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::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()) {
items_.emplace_back(new Item(bus_name, object_path, config_, bar_));
on_add_(items_.back());
spdlog::debug("Adding SNI item: {}", bus_name);
items_.emplace_back(std::make_unique<Item>(
bus_name, object_path, config_, bar_, [this](Item& item) { itemReady(item); },
[this](Item& item) { itemInvalidated(item); }, on_update_));
}
}
+165 -53
View File
@@ -5,6 +5,7 @@
#include <gtkmm/tooltip.h>
#include <spdlog/spdlog.h>
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <map>
@@ -37,13 +38,18 @@ namespace waybar::modules::SNI {
static const Glib::ustring SNI_INTERFACE_NAME = sn_item_interface_info()->name;
static const unsigned UPDATE_DEBOUNCE_TIME = 10;
Item::Item(const std::string& bn, const std::string& op, const Json::Value& config, const Bar& bar)
Item::Item(const std::string& bn, const std::string& op, const Json::Value& config, const Bar& bar,
const std::function<void(Item&)>& on_ready,
const std::function<void(Item&)>& on_invalidate, const std::function<void()>& on_updated)
: bus_name(bn),
object_path(op),
icon_size(16),
effective_icon_size(0),
icon_theme(Gtk::IconTheme::create()),
bar_(bar) {
bar_(bar),
on_ready_(on_ready),
on_invalidate_(on_invalidate),
on_updated_(on_updated) {
if (config["icon-size"].isUInt()) {
icon_size = config["icon-size"].asUInt();
}
@@ -79,8 +85,14 @@ Item::~Item() {
this->gtk_menu->popdown();
this->gtk_menu->detach();
}
if (this->dbus_menu != nullptr) {
g_object_weak_unref(G_OBJECT(this->dbus_menu), (GWeakNotify)onMenuDestroyed, this);
this->dbus_menu = nullptr;
}
}
bool Item::isReady() const { return ready_; }
bool Item::handleMouseEnter(GdkEventCrossing* const& e) {
event_box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
return false;
@@ -108,14 +120,18 @@ void Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& result) {
if (this->id.empty() || this->category.empty()) {
spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path);
invalidate();
return;
}
this->updateImage();
setReady();
} catch (const Glib::Error& err) {
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
invalidate();
} catch (const std::exception& err) {
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
invalidate();
}
}
@@ -182,17 +198,25 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
} else if (name == "Status") {
setStatus(get_variant<Glib::ustring>(value));
} else if (name == "IconName") {
icon_name = get_variant<std::string>(value);
if (has_custom_icon_) {
spdlog::trace("Item '{}': ignoring IconName update, custom icon is set", id);
} else {
icon_name = get_variant<std::string>(value);
}
} else if (name == "IconPixmap") {
icon_pixmap = this->extractPixBuf(value.gobj());
if (has_custom_icon_) {
spdlog::trace("Item '{}': ignoring IconPixmap update, custom icon is set", id);
} else {
icon_pixmap = this->extractPixBuf(value.gobj());
}
} else if (name == "OverlayIconName") {
overlay_icon_name = get_variant<std::string>(value);
} else if (name == "OverlayIconPixmap") {
// TODO: overlay_icon_pixmap
overlay_icon_pixmap = extractPixBuf(value.gobj());
} else if (name == "AttentionIconName") {
attention_icon_name = get_variant<std::string>(value);
} else if (name == "AttentionIconPixmap") {
// TODO: attention_icon_pixmap
attention_icon_pixmap = extractPixBuf(value.gobj());
} else if (name == "AttentionMovieName") {
attention_movie_name = get_variant<std::string>(value);
} else if (name == "ToolTip") {
@@ -221,18 +245,35 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
}
void Item::setStatus(const Glib::ustring& value) {
Glib::ustring lower = value.lowercase();
event_box.set_visible(!is_hidden_ && (show_passive_ || lower.compare("passive") != 0));
status_ = value.lowercase();
event_box.set_visible(!is_hidden_ && (show_passive_ || status_.compare("passive") != 0));
auto style = event_box.get_style_context();
for (const auto& class_name : style->list_classes()) {
style->remove_class(class_name);
}
if (lower.compare("needsattention") == 0) {
auto css_class = status_;
if (css_class.compare("needsattention") == 0) {
// convert status to dash-case for CSS
lower = "needs-attention";
css_class = "needs-attention";
}
style->add_class(lower);
style->add_class(css_class);
on_updated_();
}
void Item::setReady() {
if (ready_) {
return;
}
ready_ = true;
on_ready_(*this);
}
void Item::invalidate() {
if (ready_) {
ready_ = false;
}
on_invalidate_(*this);
}
void Item::setCustomIcon(const std::string& id) {
@@ -241,11 +282,17 @@ void Item::setCustomIcon(const std::string& id) {
std::string custom_icon = IconManager::instance().getIconForApp(id);
if (!custom_icon.empty()) {
if (std::filesystem::exists(custom_icon)) {
Glib::RefPtr<Gdk::Pixbuf> custom_pixbuf = Gdk::Pixbuf::create_from_file(custom_icon);
icon_name = ""; // icon_name has priority over pixmap
icon_pixmap = custom_pixbuf;
try {
Glib::RefPtr<Gdk::Pixbuf> custom_pixbuf = Gdk::Pixbuf::create_from_file(custom_icon);
icon_name = ""; // icon_name has priority over pixmap
icon_pixmap = custom_pixbuf;
has_custom_icon_ = true;
} catch (const Glib::Error& e) {
spdlog::error("Failed to load custom icon {}: {}", custom_icon, e.what());
}
} else { // if file doesn't exist it's most likely an icon_name
icon_name = custom_icon;
has_custom_icon_ = true;
}
}
}
@@ -287,8 +334,8 @@ void Item::processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& _result) {
static const std::map<std::string_view, std::set<std::string_view>> signal2props = {
{"NewTitle", {"Title"}},
{"NewIcon", {"IconName", "IconPixmap"}},
// {"NewAttentionIcon", {"AttentionIconName", "AttentionIconPixmap", "AttentionMovieName"}},
// {"NewOverlayIcon", {"OverlayIconName", "OverlayIconPixmap"}},
{"NewAttentionIcon", {"AttentionIconName", "AttentionIconPixmap", "AttentionMovieName"}},
{"NewOverlayIcon", {"OverlayIconName", "OverlayIconPixmap"}},
{"NewIconThemePath", {"IconThemePath"}},
{"NewToolTip", {"ToolTip"}},
{"NewStatus", {"Status"}},
@@ -336,11 +383,20 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
if (array != nullptr) {
g_free(array);
}
#if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 68
array = static_cast<guchar*>(g_memdup2(data, size));
#else
array = static_cast<guchar*>(g_memdup(data, size));
#endif
// We must allocate our own array because the data from GVariant is read-only
// and we need to modify it to convert ARGB to RGBA.
array = static_cast<guchar*>(g_malloc(size));
// Copy and convert ARGB to RGBA in one pass to avoid g_memdup2 overhead
const guchar* src = static_cast<const guchar*>(data);
for (gsize i = 0; i < size; i += 4) {
guchar alpha = src[i];
array[i] = src[i + 1];
array[i + 1] = src[i + 2];
array[i + 2] = src[i + 3];
array[i + 3] = alpha;
}
lwidth = width;
lheight = height;
}
@@ -349,14 +405,6 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
}
g_variant_iter_free(it);
if (array != nullptr) {
/* argb to rgba */
for (uint32_t i = 0; i < 4U * lwidth * lheight; i += 4) {
guchar alpha = array[i];
array[i] = array[i + 1];
array[i + 1] = array[i + 2];
array[i + 2] = array[i + 3];
array[i + 3] = alpha;
}
return Gdk::Pixbuf::create_from_data(array, Gdk::Colorspace::COLORSPACE_RGB, true, 8, lwidth,
lheight, 4 * lwidth, &pixbuf_data_deleter);
}
@@ -365,47 +413,36 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
void Item::updateImage() {
auto pixbuf = getIconPixbuf();
if (!pixbuf) return;
auto scaled_icon_size = getScaledIconSize();
// If the loaded icon is not square, assume that the icon height should match the
// requested icon size, but the width is allowed to be different. As such, if the
// height of the image does not match the requested icon size, resize the icon such that
// the aspect ratio is maintained, but the height matches the requested icon size.
if (pixbuf->get_height() != scaled_icon_size) {
if (pixbuf->get_height() > 0 && pixbuf->get_height() != scaled_icon_size) {
int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height();
pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
}
pixbuf = overlayPixbufs(pixbuf, getOverlayIconPixbuf());
auto surface =
Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image.get_scale_factor(), image.get_window());
image.set(surface);
}
Glib::RefPtr<Gdk::Pixbuf> Item::getIconPixbuf() {
if (!icon_name.empty()) {
try {
std::ifstream temp(icon_name);
if (temp.is_open()) {
return Gdk::Pixbuf::create_from_file(icon_name);
}
} catch (Glib::Error& e) {
// Ignore because we want to also try different methods of getting an icon.
//
// But a warning is logged, as the file apparently exists, but there was
// a failure in creating a pixbuf out of it.
spdlog::warn("Item '{}': {}", id, static_cast<std::string>(e.what()));
}
try {
// Will throw if it can not find an icon.
return getIconByName(icon_name, getScaledIconSize());
} catch (Glib::Error& e) {
spdlog::trace("Item '{}': {}", id, static_cast<std::string>(e.what()));
if (status_ == "needsattention") {
if (auto attention_pixbuf = getAttentionIconPixbuf()) {
return attention_pixbuf;
}
}
// Return the pixmap only if an icon for the given name could not be found.
if (auto pixbuf = loadIconFromNameOrFile(icon_name, true)) {
return pixbuf;
}
if (icon_pixmap) {
return icon_pixmap;
}
@@ -420,9 +457,78 @@ Glib::RefPtr<Gdk::Pixbuf> Item::getIconPixbuf() {
return getIconByName("image-missing", getScaledIconSize());
}
Glib::RefPtr<Gdk::Pixbuf> Item::getIconByName(const std::string& name, int request_size) {
icon_theme->rescan_if_needed();
Glib::RefPtr<Gdk::Pixbuf> Item::getAttentionIconPixbuf() {
if (auto pixbuf = loadIconFromNameOrFile(attention_icon_name, false)) {
return pixbuf;
}
if (auto pixbuf = loadIconFromNameOrFile(attention_movie_name, false)) {
return pixbuf;
}
return attention_icon_pixmap;
}
Glib::RefPtr<Gdk::Pixbuf> Item::getOverlayIconPixbuf() {
if (auto pixbuf = loadIconFromNameOrFile(overlay_icon_name, false)) {
return pixbuf;
}
return overlay_icon_pixmap;
}
Glib::RefPtr<Gdk::Pixbuf> Item::loadIconFromNameOrFile(const std::string& name, bool log_failure) {
if (name.empty()) {
return {};
}
try {
std::ifstream temp(name);
if (temp.is_open()) {
return Gdk::Pixbuf::create_from_file(name);
}
} catch (const Glib::Error& e) {
if (log_failure) {
spdlog::warn("Item '{}': {}", id, static_cast<std::string>(e.what()));
}
}
try {
return getIconByName(name, getScaledIconSize());
} catch (const Glib::Error& e) {
if (log_failure) {
spdlog::trace("Item '{}': {}", id, static_cast<std::string>(e.what()));
}
}
return {};
}
Glib::RefPtr<Gdk::Pixbuf> Item::overlayPixbufs(const Glib::RefPtr<Gdk::Pixbuf>& base,
const Glib::RefPtr<Gdk::Pixbuf>& overlay) {
if (!base || !overlay) {
return base;
}
auto composed = base->copy();
if (!composed) {
return base;
}
int overlay_target_size =
std::max(1, std::min(composed->get_width(), composed->get_height()) / 2);
auto scaled_overlay = overlay->scale_simple(overlay_target_size, overlay_target_size,
Gdk::InterpType::INTERP_BILINEAR);
if (!scaled_overlay) {
return composed;
}
int dest_x = std::max(0, composed->get_width() - scaled_overlay->get_width());
int dest_y = std::max(0, composed->get_height() - scaled_overlay->get_height());
scaled_overlay->composite(composed, dest_x, dest_y, scaled_overlay->get_width(),
scaled_overlay->get_height(), dest_x, dest_y, 1.0, 1.0,
Gdk::InterpType::INTERP_BILINEAR, 255);
return composed;
}
Glib::RefPtr<Gdk::Pixbuf> Item::getIconByName(const std::string& name, int request_size) {
if (!icon_theme_path.empty()) {
auto icon_info = icon_theme->lookup_icon(name.c_str(), request_size,
Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
@@ -464,6 +570,9 @@ void Item::makeMenu() {
}
bool Item::handleClick(GdkEventButton* const& ev) {
if (!proxy_) {
return false;
}
auto parameters = Glib::VariantContainerBase::create_tuple(
{Glib::Variant<int>::create(ev->x_root + bar_.x_global),
Glib::Variant<int>::create(ev->y_root + bar_.y_global)});
@@ -491,6 +600,9 @@ bool Item::handleClick(GdkEventButton* const& ev) {
}
bool Item::handleScroll(GdkEventScroll* const& ev) {
if (!proxy_) {
return false;
}
int dx = 0, dy = 0;
switch (ev->direction) {
case GDK_SCROLL_UP:
+43 -14
View File
@@ -14,13 +14,31 @@ static void initIconsConfig(const Json::Value& config) {
}
}
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)
: AModule(config, "tray", id),
box_(bar.orientation, 0),
watcher_(SNI::Watcher::getInstance()),
host_((initIconsConfig(config), nb_hosts_), config, bar,
ignore_list_(parseIgnoreList(config)),
host_((initIconsConfig(config), 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)) {
box_.set_name("tray");
event_box_.add(box_);
if (!id.empty()) {
@@ -30,19 +48,32 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
if (config_["spacing"].isUInt()) {
box_.set_spacing(config_["spacing"].asUInt());
}
if (config["show-passive-items"].isBool()) {
show_passive_ = config["show-passive-items"].asBool();
}
nb_hosts_ += 1;
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::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()) {
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));
item->event_box.signal_show().connect([this] { dp.emit(); });
item->event_box.signal_hide().connect([this] { dp.emit(); });
dp.emit();
}
@@ -52,17 +83,15 @@ void Tray::onRemove(std::unique_ptr<Item>& item) {
}
auto Tray::update() -> void {
// Show tray only when items are available
std::vector<Gtk::Widget*> children = box_.get_children();
if (show_passive_) {
event_box_.set_visible(!children.empty());
} else {
event_box_.set_visible(!std::all_of(children.begin(), children.end(), [](Gtk::Widget* child) {
return child->get_style_context()->has_class("passive");
}));
// 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));
}
// Call parent update
std::vector<Gtk::Widget*> children = box_.get_children();
event_box_.set_visible(std::any_of(children.begin(), children.end(),
[](Gtk::Widget* child) { return child->get_visible(); }));
AModule::update();
}
+6 -5
View File
@@ -31,7 +31,7 @@ Watcher::~Watcher() {
void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name) {
GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() {
waybar::util::ScopeGuard error_deleter([&error]() {
if (error) {
g_error_free(error);
}
@@ -69,7 +69,7 @@ gboolean Watcher::handleRegisterHost(Watcher* obj, GDBusMethodInvocation* invoca
if (watch != nullptr) {
g_warning("Status Notifier Host with bus name '%s' and object path '%s' is already registered",
bus_name, object_path);
sn_watcher_complete_register_item(obj->watcher_, invocation);
sn_watcher_complete_register_host(obj->watcher_, invocation);
return TRUE;
}
watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj);
@@ -98,8 +98,8 @@ gboolean Watcher::handleRegisterItem(Watcher* obj, GDBusMethodInvocation* invoca
}
auto watch = gfWatchFind(obj->items_, bus_name, object_path);
if (watch != nullptr) {
g_warning("Status Notifier Item with bus name '%s' and object path '%s' is already registered",
bus_name, object_path);
spdlog::debug("Ignoring duplicate Status Notifier Item registration for '{}' at '{}'", bus_name,
object_path);
sn_watcher_complete_register_item(obj->watcher_, invocation);
return TRUE;
}
@@ -158,7 +158,7 @@ void Watcher::nameVanished(GDBusConnection* connection, const char* name, gpoint
watch->watcher->hosts_ = g_slist_remove(watch->watcher->hosts_, watch);
if (watch->watcher->hosts_ == nullptr) {
sn_watcher_set_is_host_registered(watch->watcher->watcher_, FALSE);
sn_watcher_emit_host_registered(watch->watcher->watcher_);
sn_watcher_emit_host_unregistered(watch->watcher->watcher_);
}
} else if (watch->type == GF_WATCH_TYPE_ITEM) {
watch->watcher->items_ = g_slist_remove(watch->watcher->items_, watch);
@@ -167,6 +167,7 @@ void Watcher::nameVanished(GDBusConnection* connection, const char* name, gpoint
sn_watcher_emit_item_unregistered(watch->watcher->watcher_, tmp);
g_free(tmp);
}
gfWatchFree(watch);
}
void Watcher::updateRegisteredItems(SnWatcher* obj) {