modules/power_profiles_daemon: safely call dbus asynchronously
2 changes to address the review feedback: 1. Aleksei pointed out in this comment (https://github.com/Alexays/Waybar/pull/2971#issuecomment-1972364896) that there's no way to tell if a proxy is alive other than trying to call a method on it. We perform a little dance to check whether or not power-profiles-daemon is available on the system by calling properties.GetAll. If something responds, we assume power-profiles-daemon is installed, it's then safe to draw the widget and attach the callback to the active profile. 2. We replaced all the synchronous DBus operations by their async counterparts.
This commit is contained in:
parent
61fed6a214
commit
09bb6a055d
|
@ -19,10 +19,16 @@ class PowerProfilesDaemon : public ALabel {
|
|||
auto update() -> void override;
|
||||
void profileChangedCb(const Gio::DBus::Proxy::MapChangedProperties &,
|
||||
const std::vector<Glib::ustring> &);
|
||||
void busConnectedCb(Glib::RefPtr<Gio::AsyncResult> &r);
|
||||
void getAllPropsCb(Glib::RefPtr<Gio::AsyncResult> &r);
|
||||
void setPropCb(Glib::RefPtr<Gio::AsyncResult> &r);
|
||||
void populateInitState();
|
||||
bool handleToggle(GdkEventButton *const &e) override;
|
||||
|
||||
private:
|
||||
// True if we're connected to the dbug interface. False if we're
|
||||
// not.
|
||||
bool connected_;
|
||||
// Look for a profile name in the list of available profiles and
|
||||
// switch activeProfile_ to it.
|
||||
void switchToProfile(std::string const &);
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
namespace waybar::modules {
|
||||
|
||||
PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Value& config)
|
||||
: ALabel(config, "power-profiles-daemon", id, "{profile}", 0, false, true) {
|
||||
: ALabel(config, "power-profiles-daemon", id, "{profile}", 0, false, true), connected_(false) {
|
||||
if (config_["format"].isString()) {
|
||||
format_ = config_["format"].asString();
|
||||
} else {
|
||||
|
@ -28,6 +28,20 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu
|
|||
} else {
|
||||
tooltipFormat_ = "Power profile: {profile}\nDriver: {driver}";
|
||||
}
|
||||
// Fasten your seatbelt, we're up for quite a ride. The rest of the
|
||||
// init is performed asynchronously. There's 2 callbacks involved.
|
||||
// Here's the overall idea:
|
||||
// 1. Async connect to the system bus.
|
||||
// 2. In the system bus connect callback, try to call
|
||||
// org.freedesktop.DBus.Properties.GetAll to see if
|
||||
// power-profiles-daemon is able to respond.
|
||||
// 3. In the GetAll callback, connect the activeProfile monitoring
|
||||
// callback, consider the init to be successful. Meaning start
|
||||
// drawing the module.
|
||||
//
|
||||
// There's sadly no other way around that, we have to try to call a
|
||||
// method on the proxy to see whether or not something's responding
|
||||
// on the other side.
|
||||
|
||||
// NOTE: the DBus adresses are under migration. They should be
|
||||
// changed to org.freedesktop.UPower.PowerProfiles at some point.
|
||||
|
@ -39,29 +53,52 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu
|
|||
// adresses for compatibility sake.
|
||||
//
|
||||
// Revisit this in 2026, systems should be updated by then.
|
||||
powerProfilesProxy_ = Gio::DBus::Proxy::create_for_bus_sync(
|
||||
Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles",
|
||||
"net.hadess.PowerProfiles");
|
||||
if (!powerProfilesProxy_) {
|
||||
spdlog::error("PowerProfilesDaemon: DBus error, cannot connect to net.hasdess.PowerProfile");
|
||||
} else {
|
||||
Gio::DBus::Proxy::create_for_bus(Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles",
|
||||
"/net/hadess/PowerProfiles", "net.hadess.PowerProfiles",
|
||||
sigc::mem_fun(*this, &PowerProfilesDaemon::busConnectedCb));
|
||||
}
|
||||
|
||||
PowerProfilesDaemon::~PowerProfilesDaemon() {
|
||||
if (powerProfileChangeSignal_.connected()) {
|
||||
powerProfileChangeSignal_.disconnect();
|
||||
}
|
||||
if (powerProfilesProxy_) {
|
||||
powerProfilesProxy_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void PowerProfilesDaemon::busConnectedCb(Glib::RefPtr<Gio::AsyncResult>& r) {
|
||||
try {
|
||||
powerProfilesProxy_ = Gio::DBus::Proxy::create_for_bus_finish(r);
|
||||
using GetAllProfilesVar = Glib::Variant<std::tuple<Glib::ustring>>;
|
||||
auto callArgs = GetAllProfilesVar::create(std::make_tuple("net.hadess.PowerProfiles"));
|
||||
|
||||
auto container = Glib::VariantBase::cast_dynamic<Glib::VariantContainerBase>(callArgs);
|
||||
powerProfilesProxy_->call("org.freedesktop.DBus.Properties.GetAll",
|
||||
sigc::mem_fun(*this, &PowerProfilesDaemon::getAllPropsCb), container);
|
||||
// Connect active profile callback
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Failed to create the power profiles daemon DBus proxy");
|
||||
}
|
||||
}
|
||||
|
||||
// Callback for the GetAll call.
|
||||
//
|
||||
// We're abusing this call to make sure power-profiles-daemon is
|
||||
// available on the host. We're not really using
|
||||
void PowerProfilesDaemon::getAllPropsCb(Glib::RefPtr<Gio::AsyncResult>& r) {
|
||||
try {
|
||||
auto _ = powerProfilesProxy_->call_finish(r);
|
||||
// Power-profiles-daemon responded something, we can assume it's
|
||||
// available, we can safely attach the activeProfile monitoring
|
||||
// now.
|
||||
connected_ = true;
|
||||
powerProfileChangeSignal_ = powerProfilesProxy_->signal_properties_changed().connect(
|
||||
sigc::mem_fun(*this, &PowerProfilesDaemon::profileChangedCb));
|
||||
populateInitState();
|
||||
dp.emit();
|
||||
}
|
||||
}
|
||||
|
||||
// Look for the profile str in our internal profiles list. Using a
|
||||
// vector to store the profiles ain't the smartest move
|
||||
// complexity-wise, but it makes toggling between the mode easy. This
|
||||
// vector is 3 elements max, we'll be fine :P
|
||||
void PowerProfilesDaemon::switchToProfile(std::string const& str) {
|
||||
auto pred = [str](Profile const& p) { return p.name == str; };
|
||||
this->activeProfile_ = std::find_if(availableProfiles_.begin(), availableProfiles_.end(), pred);
|
||||
if (activeProfile_ == availableProfiles_.end()) {
|
||||
throw std::runtime_error("FATAL, can't find the active profile in the available profiles list");
|
||||
} catch (const std::exception& err) {
|
||||
spdlog::error("Failed to query power-profiles-daemon via dbus: {}", err.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,18 +132,12 @@ void PowerProfilesDaemon::populateInitState() {
|
|||
update();
|
||||
}
|
||||
|
||||
PowerProfilesDaemon::~PowerProfilesDaemon() {
|
||||
if (powerProfileChangeSignal_.connected()) {
|
||||
powerProfileChangeSignal_.disconnect();
|
||||
}
|
||||
if (powerProfilesProxy_) {
|
||||
powerProfilesProxy_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void PowerProfilesDaemon::profileChangedCb(
|
||||
const Gio::DBus::Proxy::MapChangedProperties& changedProperties,
|
||||
const std::vector<Glib::ustring>& invalidatedProperties) {
|
||||
// We're likely connected if this callback gets triggered.
|
||||
// But better be safe than sorry.
|
||||
if (connected_) {
|
||||
if (auto activeProfileVariant = changedProperties.find("ActiveProfile");
|
||||
activeProfileVariant != changedProperties.end()) {
|
||||
std::string activeProfile =
|
||||
|
@ -116,8 +147,22 @@ void PowerProfilesDaemon::profileChangedCb(
|
|||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for the profile str in our internal profiles list. Using a
|
||||
// vector to store the profiles ain't the smartest move
|
||||
// complexity-wise, but it makes toggling between the mode easy. This
|
||||
// vector is 3 elements max, we'll be fine :P
|
||||
void PowerProfilesDaemon::switchToProfile(std::string const& str) {
|
||||
auto pred = [str](Profile const& p) { return p.name == str; };
|
||||
this->activeProfile_ = std::find_if(availableProfiles_.begin(), availableProfiles_.end(), pred);
|
||||
if (activeProfile_ == availableProfiles_.end()) {
|
||||
throw std::runtime_error("FATAL, can't find the active profile in the available profiles list");
|
||||
}
|
||||
}
|
||||
|
||||
auto PowerProfilesDaemon::update() -> void {
|
||||
if (connected_) {
|
||||
auto profile = (*activeProfile_);
|
||||
// Set label
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
|
@ -138,8 +183,10 @@ auto PowerProfilesDaemon::update() -> void {
|
|||
|
||||
ALabel::update();
|
||||
}
|
||||
}
|
||||
|
||||
bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) {
|
||||
if (connected_) {
|
||||
if (e->type == GdkEventType::GDK_BUTTON_PRESS && powerProfilesProxy_) {
|
||||
activeProfile_++;
|
||||
if (activeProfile_ == availableProfiles_.end()) {
|
||||
|
@ -152,11 +199,16 @@ bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) {
|
|||
auto callArgs = SetPowerProfileVar::create(
|
||||
std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant));
|
||||
auto container = Glib::VariantBase::cast_dynamic<Glib::VariantContainerBase>(callArgs);
|
||||
powerProfilesProxy_->call_sync("org.freedesktop.DBus.Properties.Set", container);
|
||||
|
||||
update();
|
||||
powerProfilesProxy_->call("org.freedesktop.DBus.Properties.Set",
|
||||
sigc::mem_fun(*this, &PowerProfilesDaemon::setPropCb), container);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PowerProfilesDaemon::setPropCb(Glib::RefPtr<Gio::AsyncResult>& r) {
|
||||
auto _ = powerProfilesProxy_->call_finish(r);
|
||||
update();
|
||||
}
|
||||
|
||||
} // namespace waybar::modules
|
||||
|
|
Loading…
Reference in New Issue