Previously the wireplumber module connected to PipeWire once in its constructor and had no handling for the connection being lost. When PipeWire or the wireplumber service restarted (or crashed), the module went stale/blank and never recovered until Waybar itself was restarted. Connect to the WpCore "disconnected" signal and, on disconnect, schedule a bounded main-loop retry (Glib::signal_timeout) that tears down the now invalid core/object-manager/mixer-api references and rebuilds the whole connection from scratch, re-running the async API and object-manager setup. Connection setup/teardown is factored into setupConnection() and teardownConnection() so startup and reconnect share one code path. The reconnect timer is cancelled in the destructor and the existing isModuleAlive() registry guard still protects in-flight async callbacks, so teardown during a pending reconnect stays safe. Fixes #2882.
80 lines
2.9 KiB
C++
80 lines
2.9 KiB
C++
#pragma once
|
|
|
|
#include <fmt/format.h>
|
|
#include <wp/wp.h>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
|
|
#include "ALabel.hpp"
|
|
|
|
namespace waybar::modules {
|
|
|
|
class Wireplumber : public ALabel {
|
|
public:
|
|
Wireplumber(const std::string&, const Json::Value&);
|
|
virtual ~Wireplumber();
|
|
auto update() -> void override;
|
|
|
|
private:
|
|
bool setupConnection();
|
|
void teardownConnection();
|
|
void scheduleReconnect();
|
|
bool onReconnectTimeout();
|
|
static void onCoreDisconnected(waybar::modules::Wireplumber* self);
|
|
void asyncLoadRequiredApiModules();
|
|
void prepare(waybar::modules::Wireplumber* self);
|
|
void activatePlugins();
|
|
static void updateVolume(waybar::modules::Wireplumber* self, uint32_t id);
|
|
static void updateNodeName(waybar::modules::Wireplumber* self, uint32_t id);
|
|
static void updateSourceVolume(waybar::modules::Wireplumber* self, uint32_t id);
|
|
static void updateSourceName(waybar::modules::Wireplumber* self, uint32_t id); // NEW
|
|
static void onPluginActivated(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self);
|
|
static void onDefaultNodesApiLoaded(WpObject* p, GAsyncResult* res,
|
|
waybar::modules::Wireplumber* self);
|
|
static void onMixerApiLoaded(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self);
|
|
static void onObjectManagerInstalled(waybar::modules::Wireplumber* self);
|
|
static void onMixerChanged(waybar::modules::Wireplumber* self, uint32_t id);
|
|
static void onDefaultNodesApiChanged(waybar::modules::Wireplumber* self);
|
|
|
|
bool handleScroll(GdkEventScroll* e) override;
|
|
std::vector<std::string> getWPIcon();
|
|
|
|
static std::list<waybar::modules::Wireplumber*> modules;
|
|
// Returns true while `self` is still a live module. Async load/activation callbacks use this to
|
|
// avoid dereferencing a `self` that was destroyed before the callback fired (see #3974).
|
|
static bool isModuleAlive(waybar::modules::Wireplumber* self);
|
|
|
|
uint32_t resolvePhysicalSink(uint32_t start_id);
|
|
uint32_t findPlaybackNodeId(const gchar* description);
|
|
uint32_t get_linked_sink_id(WpObjectManager* om, uint32_t from_node_id);
|
|
uint32_t get_linked_node_from_output_ports(WpObjectManager* om, uint32_t from_node_id);
|
|
|
|
WpCore* wp_core_;
|
|
GPtrArray* apis_;
|
|
WpObjectManager* om_;
|
|
WpPlugin* mixer_api_;
|
|
WpPlugin* def_nodes_api_;
|
|
gchar* default_node_name_;
|
|
uint32_t pending_plugins_;
|
|
bool muted_;
|
|
double volume_;
|
|
double min_step_;
|
|
uint32_t node_id_{0};
|
|
std::string node_name_;
|
|
std::string source_name_;
|
|
gchar* type_;
|
|
uint32_t source_node_id_;
|
|
bool source_muted_;
|
|
double source_volume_;
|
|
gchar* default_source_name_;
|
|
bool only_physical_;
|
|
bool resolved_physical_;
|
|
std::string form_factor_;
|
|
// Timer used to retry connecting to PipeWire after it goes away; disconnected in the destructor
|
|
// so a pending attempt can't outlive the module. See #2882.
|
|
sigc::connection reconnect_timer_;
|
|
};
|
|
|
|
} // namespace waybar::modules
|