diff --git a/include/modules/sni/host.hpp b/include/modules/sni/host.hpp index d76ec74a..f18cf414 100644 --- a/include/modules/sni/host.hpp +++ b/include/modules/sni/host.hpp @@ -43,6 +43,8 @@ class Host { std::size_t watcher_id_; GCancellable* cancellable_ = nullptr; SnWatcher* watcher_ = nullptr; + sigc::connection retry_connection_; + unsigned retry_count_ = 0; const Json::Value& config_; const Bar& bar_; const std::function&)> on_add_; diff --git a/src/modules/sni/host.cpp b/src/modules/sni/host.cpp index 567fbf9f..2c0c42c7 100644 --- a/src/modules/sni/host.cpp +++ b/src/modules/sni/host.cpp @@ -6,6 +6,9 @@ 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::function&)>& on_add, const std::function&)>& on_remove, @@ -22,6 +25,7 @@ Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar, 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; @@ -53,6 +57,8 @@ void Host::nameAppeared(const Glib::RefPtr& conn, const G } void Host::nameVanished(const Glib::RefPtr& conn, const Glib::ustring name) { + retry_connection_.disconnect(); + retry_count_ = 0; g_cancellable_cancel(cancellable_); g_clear_object(&cancellable_); g_clear_object(&watcher_); @@ -72,11 +78,30 @@ void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) { return; } auto host = static_cast(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); }