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.pull/2971/head
parent
61fed6a214
commit
09bb6a055d
|
@ -14,18 +14,24 @@ struct Profile {
|
||||||
|
|
||||||
class PowerProfilesDaemon : public ALabel {
|
class PowerProfilesDaemon : public ALabel {
|
||||||
public:
|
public:
|
||||||
PowerProfilesDaemon(const std::string&, const Json::Value&);
|
PowerProfilesDaemon(const std::string &, const Json::Value &);
|
||||||
~PowerProfilesDaemon() override;
|
~PowerProfilesDaemon() override;
|
||||||
auto update() -> void override;
|
auto update() -> void override;
|
||||||
void profileChangedCb(const Gio::DBus::Proxy::MapChangedProperties&,
|
void profileChangedCb(const Gio::DBus::Proxy::MapChangedProperties &,
|
||||||
const std::vector<Glib::ustring>&);
|
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();
|
void populateInitState();
|
||||||
bool handleToggle(GdkEventButton* const& e) override;
|
bool handleToggle(GdkEventButton *const &e) override;
|
||||||
|
|
||||||
private:
|
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
|
// Look for a profile name in the list of available profiles and
|
||||||
// switch activeProfile_ to it.
|
// switch activeProfile_ to it.
|
||||||
void switchToProfile(std::string const&);
|
void switchToProfile(std::string const &);
|
||||||
// Used to toggle/display the profiles
|
// Used to toggle/display the profiles
|
||||||
std::vector<Profile> availableProfiles_;
|
std::vector<Profile> availableProfiles_;
|
||||||
// Points to the active profile in the profiles list
|
// Points to the active profile in the profiles list
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
|
|
||||||
PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Value& config)
|
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()) {
|
if (config_["format"].isString()) {
|
||||||
format_ = config_["format"].asString();
|
format_ = config_["format"].asString();
|
||||||
} else {
|
} else {
|
||||||
|
@ -28,6 +28,20 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu
|
||||||
} else {
|
} else {
|
||||||
tooltipFormat_ = "Power profile: {profile}\nDriver: {driver}";
|
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
|
// NOTE: the DBus adresses are under migration. They should be
|
||||||
// changed to org.freedesktop.UPower.PowerProfiles at some point.
|
// 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.
|
// adresses for compatibility sake.
|
||||||
//
|
//
|
||||||
// Revisit this in 2026, systems should be updated by then.
|
// Revisit this in 2026, systems should be updated by then.
|
||||||
powerProfilesProxy_ = Gio::DBus::Proxy::create_for_bus_sync(
|
Gio::DBus::Proxy::create_for_bus(Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles",
|
||||||
Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles",
|
"/net/hadess/PowerProfiles", "net.hadess.PowerProfiles",
|
||||||
"net.hadess.PowerProfiles");
|
sigc::mem_fun(*this, &PowerProfilesDaemon::busConnectedCb));
|
||||||
if (!powerProfilesProxy_) {
|
}
|
||||||
spdlog::error("PowerProfilesDaemon: DBus error, cannot connect to net.hasdess.PowerProfile");
|
|
||||||
} else {
|
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
|
// 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(
|
powerProfileChangeSignal_ = powerProfilesProxy_->signal_properties_changed().connect(
|
||||||
sigc::mem_fun(*this, &PowerProfilesDaemon::profileChangedCb));
|
sigc::mem_fun(*this, &PowerProfilesDaemon::profileChangedCb));
|
||||||
populateInitState();
|
populateInitState();
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
} catch (const std::exception& err) {
|
||||||
}
|
spdlog::error("Failed to query power-profiles-daemon via dbus: {}", err.what());
|
||||||
|
|
||||||
// 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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,18 +132,12 @@ void PowerProfilesDaemon::populateInitState() {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
PowerProfilesDaemon::~PowerProfilesDaemon() {
|
|
||||||
if (powerProfileChangeSignal_.connected()) {
|
|
||||||
powerProfileChangeSignal_.disconnect();
|
|
||||||
}
|
|
||||||
if (powerProfilesProxy_) {
|
|
||||||
powerProfilesProxy_.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PowerProfilesDaemon::profileChangedCb(
|
void PowerProfilesDaemon::profileChangedCb(
|
||||||
const Gio::DBus::Proxy::MapChangedProperties& changedProperties,
|
const Gio::DBus::Proxy::MapChangedProperties& changedProperties,
|
||||||
const std::vector<Glib::ustring>& invalidatedProperties) {
|
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");
|
if (auto activeProfileVariant = changedProperties.find("ActiveProfile");
|
||||||
activeProfileVariant != changedProperties.end()) {
|
activeProfileVariant != changedProperties.end()) {
|
||||||
std::string activeProfile =
|
std::string activeProfile =
|
||||||
|
@ -115,9 +146,23 @@ void PowerProfilesDaemon::profileChangedCb(
|
||||||
switchToProfile(activeProfile);
|
switchToProfile(activeProfile);
|
||||||
update();
|
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 {
|
auto PowerProfilesDaemon::update() -> void {
|
||||||
|
if (connected_) {
|
||||||
auto profile = (*activeProfile_);
|
auto profile = (*activeProfile_);
|
||||||
// Set label
|
// Set label
|
||||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||||
|
@ -137,9 +182,11 @@ auto PowerProfilesDaemon::update() -> void {
|
||||||
currentStyle_ = profile.name;
|
currentStyle_ = profile.name;
|
||||||
|
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) {
|
bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) {
|
||||||
|
if (connected_) {
|
||||||
if (e->type == GdkEventType::GDK_BUTTON_PRESS && powerProfilesProxy_) {
|
if (e->type == GdkEventType::GDK_BUTTON_PRESS && powerProfilesProxy_) {
|
||||||
activeProfile_++;
|
activeProfile_++;
|
||||||
if (activeProfile_ == availableProfiles_.end()) {
|
if (activeProfile_ == availableProfiles_.end()) {
|
||||||
|
@ -152,11 +199,16 @@ bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) {
|
||||||
auto callArgs = SetPowerProfileVar::create(
|
auto callArgs = SetPowerProfileVar::create(
|
||||||
std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant));
|
std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant));
|
||||||
auto container = Glib::VariantBase::cast_dynamic<Glib::VariantContainerBase>(callArgs);
|
auto container = Glib::VariantBase::cast_dynamic<Glib::VariantContainerBase>(callArgs);
|
||||||
powerProfilesProxy_->call_sync("org.freedesktop.DBus.Properties.Set", container);
|
powerProfilesProxy_->call("org.freedesktop.DBus.Properties.Set",
|
||||||
|
sigc::mem_fun(*this, &PowerProfilesDaemon::setPropCb), container);
|
||||||
update();
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PowerProfilesDaemon::setPropCb(Glib::RefPtr<Gio::AsyncResult>& r) {
|
||||||
|
auto _ = powerProfilesProxy_->call_finish(r);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
|
|
Loading…
Reference in New Issue