From c38d05b04f12c0c8cd01538ead1f6bcc5d1d9603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Sun, 18 Feb 2024 22:06:21 +0100 Subject: [PATCH 1/9] Introduce power-profiles-daemon module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We introduce a module in charge to display and toggle on click the power profiles via power-profiles-daemon. https://gitlab.freedesktop.org/upower/power-profiles-daemon This daemon is pretty widespread. It's the component used by Gnome and KDE to manage the power profiles. The power management daemon is a pretty important software component for laptops and other battery-powered devices. We're using the daemon DBus interface to: - Fetch the available power profiles. - Track the active power profile. - Change the active power profile. The original author recently gave up maintenance on the project. The Upower group took over the maintenance burden… …and created a new DBus name for the project. The old name is still advertised for now. We use the old name for compatibility sake: most distributions did not release 0.20, which introduces this new DBus name. We'll likely revisit this in the future and point to the new bus name. See the inline comment for more details. Given how widespread this daemon is, I activated the module in the default configuration. --- README.md | 1 + include/modules/power_profiles_daemon.hpp | 38 ++++++ meson.build | 1 + resources/config.jsonc | 2 +- resources/style.css | 16 +++ src/factory.cpp | 4 + src/modules/power_profiles_daemon.cpp | 146 ++++++++++++++++++++++ 7 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 include/modules/power_profiles_daemon.hpp create mode 100644 src/modules/power_profiles_daemon.cpp diff --git a/README.md b/README.md index 07b11152..65be764c 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ - Local time - Battery - UPower +- Power profiles daemon - Network - Bluetooth - Pulseaudio diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp new file mode 100644 index 00000000..71654e36 --- /dev/null +++ b/include/modules/power_profiles_daemon.hpp @@ -0,0 +1,38 @@ +# pragma once + +#include + +#include "ALabel.hpp" +#include "giomm/dbusproxy.h" + +namespace waybar::modules { + +typedef struct { + std::string name; + std::string driver; +} Profile; + +class PowerProfilesDaemon : public ALabel { + public: + PowerProfilesDaemon(const std::string&, const Json::Value&); + ~PowerProfilesDaemon(); + auto update() -> void override; + void profileChanged_cb( const Gio::DBus::Proxy::MapChangedProperties&, const std::vector&); + void populateInitState(); + virtual bool handleToggle(GdkEventButton* const& e); + private: + // Look for a profile name in the list of available profiles and + // switch activeProfile_ to it. + void switchToProfile_(std::string); + // Used to toggle/display the profiles + std::vector availableProfiles_; + // Points to the active profile in the profiles list + std::vector::iterator activeProfile_; + // Current CSS class applied to the label + std::string currentStyle_; + // DBus Proxy used to track the current active profile + Glib::RefPtr power_profiles_proxy_; + sigc::connection powerProfileChangeSignal_; +}; + +} diff --git a/meson.build b/meson.build index b995d569..10c82620 100644 --- a/meson.build +++ b/meson.build @@ -212,6 +212,7 @@ if is_linux 'src/modules/cpu_usage/linux.cpp', 'src/modules/memory/common.cpp', 'src/modules/memory/linux.cpp', + 'src/modules/power_profiles_daemon.cpp', 'src/modules/systemd_failed_units.cpp', ) man_files += files( diff --git a/resources/config.jsonc b/resources/config.jsonc index f225e4fc..00612136 100644 --- a/resources/config.jsonc +++ b/resources/config.jsonc @@ -20,6 +20,7 @@ "idle_inhibitor", "pulseaudio", "network", + "power_profiles_daemon", "cpu", "memory", "temperature", @@ -188,4 +189,3 @@ // "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name } } - diff --git a/resources/style.css b/resources/style.css index 6e4fcebc..7f708ff4 100644 --- a/resources/style.css +++ b/resources/style.css @@ -87,6 +87,7 @@ button:hover { #mode, #idle_inhibitor, #scratchpad, +#power-profiles-daemon, #mpd { padding: 0 10px; color: #ffffff; @@ -139,6 +140,21 @@ button:hover { animation-direction: alternate; } +#power-profiles-daemon.performance { + background-color: #f53c3c; + color: #ffffff; +} + +#power-profiles-daemon.balanced { + background-color: #2980b9; + color: #ffffff; +} + +#power-profiles-daemon.power-saver { + background-color: #2ecc71; + color: #000000; +} + label:focus { background-color: #000000; } diff --git a/src/factory.cpp b/src/factory.cpp index 6b709f33..7dc6709e 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -86,6 +86,7 @@ #endif #if defined(__linux__) #include "modules/bluetooth.hpp" +#include "modules/power_profiles_daemon.hpp" #endif #ifdef HAVE_LOGIND_INHIBITOR #include "modules/inhibitor.hpp" @@ -282,6 +283,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, if (ref == "bluetooth") { return new waybar::modules::Bluetooth(id, config_[name]); } + if (ref == "power_profiles_daemon") { + return new waybar::modules::PowerProfilesDaemon(id, config_[name]); + } #endif #ifdef HAVE_LOGIND_INHIBITOR if (ref == "inhibitor") { diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp new file mode 100644 index 00000000..f4dfd1c8 --- /dev/null +++ b/src/modules/power_profiles_daemon.cpp @@ -0,0 +1,146 @@ +#include "modules/power_profiles_daemon.hpp" + +// In the 80000 version of fmt library authors decided to optimize imports +// and moved declarations required for fmt::dynamic_format_arg_store in new +// header fmt/args.h +#if (FMT_VERSION >= 80000) +#include +#else +#include +#endif + +#include +#include +#include + + + +namespace waybar::modules { + +PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Value& config) + : ALabel(config, "power-profiles-daemon", id, "{profile}", 0, false, true) +{ + // NOTE: the DBus adresses are under migration. They should be + // changed to org.freedesktop.UPower.PowerProfiles at some point. + // + // See + // https://gitlab.freedesktop.org/upower/power-profiles-daemon/-/releases/0.20 + // + // The old name is still announced for now. Let's rather use the old + // adresses for compatibility sake. + // + // Revisit this in 2026, systems should be updated by then. + power_profiles_proxy_ = Gio::DBus::Proxy::create_for_bus_sync(Gio::DBus::BusType::BUS_TYPE_SYSTEM, + "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", + "net.hadess.PowerProfiles"); + if (!power_profiles_proxy_) { + spdlog::error("PowerProfilesDaemon: DBus error, cannot connect to net.hasdess.PowerProfile"); + } else { + // Connect active profile callback + powerProfileChangeSignal_ = power_profiles_proxy_->signal_properties_changed() + .connect(sigc::mem_fun(*this, &PowerProfilesDaemon::profileChanged_cb)); + 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 str) { + auto pred = [str](Profile p) { return p.name == str; }; + 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"); + } +} + +void PowerProfilesDaemon::populateInitState() { + // Retrieve current active profile + Glib::Variant profileStr; + power_profiles_proxy_->get_cached_property(profileStr, "ActiveProfile"); + + // Retrieve profiles list, it's aa{sv}. + using ProfilesType = std::vector>>; + Glib::Variant profilesVariant; + power_profiles_proxy_->get_cached_property(profilesVariant, "Profiles"); + Glib::ustring name, driver; + Profile profile; + for (auto & variantDict: profilesVariant.get()) { + if (auto p = variantDict.find("Profile"); p != variantDict.end()) { + name = p->second.get(); + } + if (auto d = variantDict.find("Driver"); d != variantDict.end()) { + driver = d->second.get(); + } + profile = { name, driver }; + availableProfiles_.push_back(profile); + } + + // Find the index of the current activated mode (to toggle) + std::string str = profileStr.get(); + switchToProfile_(str); + + update(); +} + +PowerProfilesDaemon::~PowerProfilesDaemon() { + if (powerProfileChangeSignal_.connected()) { + powerProfileChangeSignal_.disconnect(); + } + if (power_profiles_proxy_) { + power_profiles_proxy_.reset(); + } +} + +void PowerProfilesDaemon::profileChanged_cb(const Gio::DBus::Proxy::MapChangedProperties& changedProperties, + const std::vector& invalidatedProperties) { + if (auto activeProfileVariant = changedProperties.find("ActiveProfile"); activeProfileVariant != changedProperties.end()) { + std::string activeProfile = Glib::VariantBase::cast_dynamic>(activeProfileVariant->second).get(); + switchToProfile_(activeProfile); + update(); + } +} + +auto PowerProfilesDaemon::update () -> void { + auto profile = (*activeProfile_); + // Set label + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("profile", profile.name)); + label_.set_markup(fmt::vformat("⚡ {profile}", store)); + if (tooltipEnabled()) { + label_.set_tooltip_text(fmt::format("Driver: {}", profile.driver)); + } + + // Set CSS class + if (!currentStyle_.empty()) { + label_.get_style_context()->remove_class(currentStyle_); + } + label_.get_style_context()->add_class(profile.name); + currentStyle_ = profile.name; + + ALabel::update(); +} + + +bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { + if (e->type == GdkEventType::GDK_BUTTON_PRESS && power_profiles_proxy_) { + activeProfile_++; + if (activeProfile_ == availableProfiles_.end()) { + activeProfile_ = availableProfiles_.begin(); + } + + using VarStr = Glib::Variant; + using SetPowerProfileVar = Glib::Variant>; + VarStr activeProfileVariant = VarStr::create(activeProfile_->name); + auto call_args = SetPowerProfileVar::create(std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); + auto container = Glib::VariantBase::cast_dynamic(call_args); + power_profiles_proxy_->call_sync("org.freedesktop.DBus.Properties.Set", container); + + update(); + } + return true; +} + +} From 968f469289b99801476ee5e8f2d38b1e296f2cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Mon, 26 Feb 2024 14:40:28 +0100 Subject: [PATCH 2/9] modules/power-profiles-daemon: run clang format --- include/modules/power_profiles_daemon.hpp | 10 +++--- src/modules/power_profiles_daemon.cpp | 43 ++++++++++++----------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index 71654e36..40a512f1 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -1,4 +1,4 @@ -# pragma once +#pragma once #include @@ -12,14 +12,16 @@ typedef struct { std::string driver; } Profile; -class PowerProfilesDaemon : public ALabel { +class PowerProfilesDaemon : public ALabel { public: PowerProfilesDaemon(const std::string&, const Json::Value&); ~PowerProfilesDaemon(); auto update() -> void override; - void profileChanged_cb( const Gio::DBus::Proxy::MapChangedProperties&, const std::vector&); + void profileChanged_cb(const Gio::DBus::Proxy::MapChangedProperties&, + const std::vector&); void populateInitState(); virtual bool handleToggle(GdkEventButton* const& e); + private: // Look for a profile name in the list of available profiles and // switch activeProfile_ to it. @@ -35,4 +37,4 @@ class PowerProfilesDaemon : public ALabel { sigc::connection powerProfileChangeSignal_; }; -} +} // namespace waybar::modules diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index f4dfd1c8..e5e379db 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -9,17 +9,14 @@ #include #endif -#include #include #include - - +#include 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) { // NOTE: the DBus adresses are under migration. They should be // changed to org.freedesktop.UPower.PowerProfiles at some point. // @@ -30,15 +27,15 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu // adresses for compatibility sake. // // Revisit this in 2026, systems should be updated by then. - power_profiles_proxy_ = Gio::DBus::Proxy::create_for_bus_sync(Gio::DBus::BusType::BUS_TYPE_SYSTEM, - "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", - "net.hadess.PowerProfiles"); + power_profiles_proxy_ = Gio::DBus::Proxy::create_for_bus_sync( + Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", + "net.hadess.PowerProfiles"); if (!power_profiles_proxy_) { spdlog::error("PowerProfilesDaemon: DBus error, cannot connect to net.hasdess.PowerProfile"); } else { // Connect active profile callback - powerProfileChangeSignal_ = power_profiles_proxy_->signal_properties_changed() - .connect(sigc::mem_fun(*this, &PowerProfilesDaemon::profileChanged_cb)); + powerProfileChangeSignal_ = power_profiles_proxy_->signal_properties_changed().connect( + sigc::mem_fun(*this, &PowerProfilesDaemon::profileChanged_cb)); populateInitState(); dp.emit(); } @@ -67,14 +64,14 @@ void PowerProfilesDaemon::populateInitState() { power_profiles_proxy_->get_cached_property(profilesVariant, "Profiles"); Glib::ustring name, driver; Profile profile; - for (auto & variantDict: profilesVariant.get()) { + for (auto& variantDict : profilesVariant.get()) { if (auto p = variantDict.find("Profile"); p != variantDict.end()) { name = p->second.get(); } if (auto d = variantDict.find("Driver"); d != variantDict.end()) { driver = d->second.get(); } - profile = { name, driver }; + profile = {name, driver}; availableProfiles_.push_back(profile); } @@ -94,16 +91,20 @@ PowerProfilesDaemon::~PowerProfilesDaemon() { } } -void PowerProfilesDaemon::profileChanged_cb(const Gio::DBus::Proxy::MapChangedProperties& changedProperties, - const std::vector& invalidatedProperties) { - if (auto activeProfileVariant = changedProperties.find("ActiveProfile"); activeProfileVariant != changedProperties.end()) { - std::string activeProfile = Glib::VariantBase::cast_dynamic>(activeProfileVariant->second).get(); +void PowerProfilesDaemon::profileChanged_cb( + const Gio::DBus::Proxy::MapChangedProperties& changedProperties, + const std::vector& invalidatedProperties) { + if (auto activeProfileVariant = changedProperties.find("ActiveProfile"); + activeProfileVariant != changedProperties.end()) { + std::string activeProfile = + Glib::VariantBase::cast_dynamic>(activeProfileVariant->second) + .get(); switchToProfile_(activeProfile); update(); } } -auto PowerProfilesDaemon::update () -> void { +auto PowerProfilesDaemon::update() -> void { auto profile = (*activeProfile_); // Set label fmt::dynamic_format_arg_store store; @@ -123,7 +124,6 @@ auto PowerProfilesDaemon::update () -> void { ALabel::update(); } - bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { if (e->type == GdkEventType::GDK_BUTTON_PRESS && power_profiles_proxy_) { activeProfile_++; @@ -132,9 +132,10 @@ bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { } using VarStr = Glib::Variant; - using SetPowerProfileVar = Glib::Variant>; + using SetPowerProfileVar = Glib::Variant>; VarStr activeProfileVariant = VarStr::create(activeProfile_->name); - auto call_args = SetPowerProfileVar::create(std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); + auto call_args = SetPowerProfileVar::create( + std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); auto container = Glib::VariantBase::cast_dynamic(call_args); power_profiles_proxy_->call_sync("org.freedesktop.DBus.Properties.Set", container); @@ -143,4 +144,4 @@ bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { return true; } -} +} // namespace waybar::modules From 162b41c4d02c4ba5147304a999bb7858603e329c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Mon, 26 Feb 2024 15:07:21 +0100 Subject: [PATCH 3/9] modules/power-profiles-daemon: apply clang-tidy suggestions --- include/modules/power_profiles_daemon.hpp | 16 +++++----- src/modules/power_profiles_daemon.cpp | 39 ++++++++++++----------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index 40a512f1..92ead748 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -7,25 +7,25 @@ namespace waybar::modules { -typedef struct { +struct Profile { std::string name; std::string driver; -} Profile; +}; class PowerProfilesDaemon : public ALabel { public: PowerProfilesDaemon(const std::string&, const Json::Value&); - ~PowerProfilesDaemon(); + ~PowerProfilesDaemon() override; auto update() -> void override; - void profileChanged_cb(const Gio::DBus::Proxy::MapChangedProperties&, - const std::vector&); + void profileChangedCb(const Gio::DBus::Proxy::MapChangedProperties&, + const std::vector&); void populateInitState(); - virtual bool handleToggle(GdkEventButton* const& e); + bool handleToggle(GdkEventButton* const& e) override; private: // Look for a profile name in the list of available profiles and // switch activeProfile_ to it. - void switchToProfile_(std::string); + void switchToProfile(std::string const&); // Used to toggle/display the profiles std::vector availableProfiles_; // Points to the active profile in the profiles list @@ -33,7 +33,7 @@ class PowerProfilesDaemon : public ALabel { // Current CSS class applied to the label std::string currentStyle_; // DBus Proxy used to track the current active profile - Glib::RefPtr power_profiles_proxy_; + Glib::RefPtr powerProfilesProxy_; sigc::connection powerProfileChangeSignal_; }; diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index e5e379db..3dd43b87 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -27,15 +27,15 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu // adresses for compatibility sake. // // Revisit this in 2026, systems should be updated by then. - power_profiles_proxy_ = Gio::DBus::Proxy::create_for_bus_sync( + powerProfilesProxy_ = Gio::DBus::Proxy::create_for_bus_sync( Gio::DBus::BusType::BUS_TYPE_SYSTEM, "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", "net.hadess.PowerProfiles"); - if (!power_profiles_proxy_) { + if (!powerProfilesProxy_) { spdlog::error("PowerProfilesDaemon: DBus error, cannot connect to net.hasdess.PowerProfile"); } else { // Connect active profile callback - powerProfileChangeSignal_ = power_profiles_proxy_->signal_properties_changed().connect( - sigc::mem_fun(*this, &PowerProfilesDaemon::profileChanged_cb)); + powerProfileChangeSignal_ = powerProfilesProxy_->signal_properties_changed().connect( + sigc::mem_fun(*this, &PowerProfilesDaemon::profileChangedCb)); populateInitState(); dp.emit(); } @@ -45,9 +45,9 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu // 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 str) { - auto pred = [str](Profile p) { return p.name == str; }; - activeProfile_ = std::find_if(availableProfiles_.begin(), availableProfiles_.end(), pred); +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"); } @@ -56,13 +56,14 @@ void PowerProfilesDaemon::switchToProfile_(std::string str) { void PowerProfilesDaemon::populateInitState() { // Retrieve current active profile Glib::Variant profileStr; - power_profiles_proxy_->get_cached_property(profileStr, "ActiveProfile"); + powerProfilesProxy_->get_cached_property(profileStr, "ActiveProfile"); // Retrieve profiles list, it's aa{sv}. using ProfilesType = std::vector>>; Glib::Variant profilesVariant; - power_profiles_proxy_->get_cached_property(profilesVariant, "Profiles"); - Glib::ustring name, driver; + powerProfilesProxy_->get_cached_property(profilesVariant, "Profiles"); + Glib::ustring name; + Glib::ustring driver; Profile profile; for (auto& variantDict : profilesVariant.get()) { if (auto p = variantDict.find("Profile"); p != variantDict.end()) { @@ -77,7 +78,7 @@ void PowerProfilesDaemon::populateInitState() { // Find the index of the current activated mode (to toggle) std::string str = profileStr.get(); - switchToProfile_(str); + switchToProfile(str); update(); } @@ -86,12 +87,12 @@ PowerProfilesDaemon::~PowerProfilesDaemon() { if (powerProfileChangeSignal_.connected()) { powerProfileChangeSignal_.disconnect(); } - if (power_profiles_proxy_) { - power_profiles_proxy_.reset(); + if (powerProfilesProxy_) { + powerProfilesProxy_.reset(); } } -void PowerProfilesDaemon::profileChanged_cb( +void PowerProfilesDaemon::profileChangedCb( const Gio::DBus::Proxy::MapChangedProperties& changedProperties, const std::vector& invalidatedProperties) { if (auto activeProfileVariant = changedProperties.find("ActiveProfile"); @@ -99,7 +100,7 @@ void PowerProfilesDaemon::profileChanged_cb( std::string activeProfile = Glib::VariantBase::cast_dynamic>(activeProfileVariant->second) .get(); - switchToProfile_(activeProfile); + switchToProfile(activeProfile); update(); } } @@ -125,7 +126,7 @@ auto PowerProfilesDaemon::update() -> void { } bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { - if (e->type == GdkEventType::GDK_BUTTON_PRESS && power_profiles_proxy_) { + if (e->type == GdkEventType::GDK_BUTTON_PRESS && powerProfilesProxy_) { activeProfile_++; if (activeProfile_ == availableProfiles_.end()) { activeProfile_ = availableProfiles_.begin(); @@ -134,10 +135,10 @@ bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { using VarStr = Glib::Variant; using SetPowerProfileVar = Glib::Variant>; VarStr activeProfileVariant = VarStr::create(activeProfile_->name); - auto call_args = SetPowerProfileVar::create( + auto callArgs = SetPowerProfileVar::create( std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); - auto container = Glib::VariantBase::cast_dynamic(call_args); - power_profiles_proxy_->call_sync("org.freedesktop.DBus.Properties.Set", container); + auto container = Glib::VariantBase::cast_dynamic(callArgs); + powerProfilesProxy_->call_sync("org.freedesktop.DBus.Properties.Set", container); update(); } From 61fed6a21474752e382f17724091b91dee273e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Fri, 1 Mar 2024 11:13:57 +0100 Subject: [PATCH 4/9] modules/power_profiles_daemon: add custom format from config We move to a single icon label format to save space on the bar. We still display the profile name and the driver in the tooltip. --- include/modules/power_profiles_daemon.hpp | 3 +++ resources/config.jsonc | 11 +++++++++++ src/modules/power_profiles_daemon.cpp | 18 ++++++++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index 92ead748..72ffb314 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -32,6 +32,9 @@ class PowerProfilesDaemon : public ALabel { std::vector::iterator activeProfile_; // Current CSS class applied to the label std::string currentStyle_; + // Format strings + std::string labelFormat_; + std::string tooltipFormat_; // DBus Proxy used to track the current active profile Glib::RefPtr powerProfilesProxy_; sigc::connection powerProfileChangeSignal_; diff --git a/resources/config.jsonc b/resources/config.jsonc index 00612136..4e300a33 100644 --- a/resources/config.jsonc +++ b/resources/config.jsonc @@ -148,6 +148,17 @@ "battery#bat2": { "bat": "BAT2" }, + "power_profiles_daemon": { + "format": "{icon}", + "tooltip-format": "Power profile: {profile}\nDriver: {driver}", + "tooltip": true, + "format-icons": { + "default": "", + "performance": "", + "balanced": "", + "power-saver": "" + } + }, "network": { // "interface": "wlp2*", // (Optional) To force the use of this interface "format-wifi": "{essid} ({signalStrength}%) ", diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index 3dd43b87..6b684662 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -17,6 +17,18 @@ namespace waybar::modules { PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Value& config) : ALabel(config, "power-profiles-daemon", id, "{profile}", 0, false, true) { + if (config_["format"].isString()) { + format_ = config_["format"].asString(); + } else { + format_ = "{icon}"; + } + + if (config_["tooltip-format"].isString()) { + tooltipFormat_ = config_["tooltip-format"].asString(); + } else { + tooltipFormat_ = "Power profile: {profile}\nDriver: {driver}"; + } + // NOTE: the DBus adresses are under migration. They should be // changed to org.freedesktop.UPower.PowerProfiles at some point. // @@ -110,9 +122,11 @@ auto PowerProfilesDaemon::update() -> void { // Set label fmt::dynamic_format_arg_store store; store.push_back(fmt::arg("profile", profile.name)); - label_.set_markup(fmt::vformat("⚡ {profile}", store)); + store.push_back(fmt::arg("driver", profile.driver)); + store.push_back(fmt::arg("icon", getIcon(0, profile.name))); + label_.set_markup(fmt::vformat(format_, store)); if (tooltipEnabled()) { - label_.set_tooltip_text(fmt::format("Driver: {}", profile.driver)); + label_.set_tooltip_text(fmt::vformat(tooltipFormat_, store)); } // Set CSS class From 09bb6a055dec244d0d8a26fccc79752015bd03ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Fri, 1 Mar 2024 12:33:36 +0100 Subject: [PATCH 5/9] 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. --- include/modules/power_profiles_daemon.hpp | 16 +- src/modules/power_profiles_daemon.cpp | 184 ++++++++++++++-------- 2 files changed, 129 insertions(+), 71 deletions(-) diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index 72ffb314..bfb5c99d 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -14,18 +14,24 @@ struct Profile { class PowerProfilesDaemon : public ALabel { public: - PowerProfilesDaemon(const std::string&, const Json::Value&); + PowerProfilesDaemon(const std::string &, const Json::Value &); ~PowerProfilesDaemon() override; auto update() -> void override; - void profileChangedCb(const Gio::DBus::Proxy::MapChangedProperties&, - const std::vector&); + void profileChangedCb(const Gio::DBus::Proxy::MapChangedProperties &, + const std::vector &); + void busConnectedCb(Glib::RefPtr &r); + void getAllPropsCb(Glib::RefPtr &r); + void setPropCb(Glib::RefPtr &r); void populateInitState(); - bool handleToggle(GdkEventButton* const& e) override; + 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&); + void switchToProfile(std::string const &); // Used to toggle/display the profiles std::vector availableProfiles_; // Points to the active profile in the profiles list diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index 6b684662..bd6a52a7 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -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& r) { + try { + powerProfilesProxy_ = Gio::DBus::Proxy::create_for_bus_finish(r); + using GetAllProfilesVar = Glib::Variant>; + auto callArgs = GetAllProfilesVar::create(std::make_tuple("net.hadess.PowerProfiles")); + + auto container = Glib::VariantBase::cast_dynamic(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& 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,68 +132,83 @@ 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& invalidatedProperties) { - if (auto activeProfileVariant = changedProperties.find("ActiveProfile"); - activeProfileVariant != changedProperties.end()) { - std::string activeProfile = - Glib::VariantBase::cast_dynamic>(activeProfileVariant->second) - .get(); - switchToProfile(activeProfile); - update(); + // 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 = + Glib::VariantBase::cast_dynamic>(activeProfileVariant->second) + .get(); + switchToProfile(activeProfile); + 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 profile = (*activeProfile_); - // Set label - fmt::dynamic_format_arg_store store; - store.push_back(fmt::arg("profile", profile.name)); - store.push_back(fmt::arg("driver", profile.driver)); - store.push_back(fmt::arg("icon", getIcon(0, profile.name))); - label_.set_markup(fmt::vformat(format_, store)); - if (tooltipEnabled()) { - label_.set_tooltip_text(fmt::vformat(tooltipFormat_, store)); - } + if (connected_) { + auto profile = (*activeProfile_); + // Set label + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("profile", profile.name)); + store.push_back(fmt::arg("driver", profile.driver)); + store.push_back(fmt::arg("icon", getIcon(0, profile.name))); + label_.set_markup(fmt::vformat(format_, store)); + if (tooltipEnabled()) { + label_.set_tooltip_text(fmt::vformat(tooltipFormat_, store)); + } - // Set CSS class - if (!currentStyle_.empty()) { - label_.get_style_context()->remove_class(currentStyle_); - } - label_.get_style_context()->add_class(profile.name); - currentStyle_ = profile.name; + // Set CSS class + if (!currentStyle_.empty()) { + label_.get_style_context()->remove_class(currentStyle_); + } + label_.get_style_context()->add_class(profile.name); + currentStyle_ = profile.name; - ALabel::update(); + ALabel::update(); + } } bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { - if (e->type == GdkEventType::GDK_BUTTON_PRESS && powerProfilesProxy_) { - activeProfile_++; - if (activeProfile_ == availableProfiles_.end()) { - activeProfile_ = availableProfiles_.begin(); + if (connected_) { + if (e->type == GdkEventType::GDK_BUTTON_PRESS && powerProfilesProxy_) { + activeProfile_++; + if (activeProfile_ == availableProfiles_.end()) { + activeProfile_ = availableProfiles_.begin(); + } + + using VarStr = Glib::Variant; + using SetPowerProfileVar = Glib::Variant>; + VarStr activeProfileVariant = VarStr::create(activeProfile_->name); + auto callArgs = SetPowerProfileVar::create( + std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); + auto container = Glib::VariantBase::cast_dynamic(callArgs); + powerProfilesProxy_->call("org.freedesktop.DBus.Properties.Set", + sigc::mem_fun(*this, &PowerProfilesDaemon::setPropCb), container); } - - using VarStr = Glib::Variant; - using SetPowerProfileVar = Glib::Variant>; - VarStr activeProfileVariant = VarStr::create(activeProfile_->name); - auto callArgs = SetPowerProfileVar::create( - std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); - auto container = Glib::VariantBase::cast_dynamic(callArgs); - powerProfilesProxy_->call_sync("org.freedesktop.DBus.Properties.Set", container); - - update(); } return true; } +void PowerProfilesDaemon::setPropCb(Glib::RefPtr& r) { + auto _ = powerProfilesProxy_->call_finish(r); + update(); +} + } // namespace waybar::modules From bddc8703403e37d37f66841cd3421ef6a6406794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Fri, 1 Mar 2024 15:07:58 +0100 Subject: [PATCH 6/9] modules/power-profiles-daemon: add man page There was no way to display the default value of format-icons without breaking the table :( --- man/waybar-power-profiles-daemon.5.scd | 72 ++++++++++++++++++++++++++ meson.build | 2 +- 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 man/waybar-power-profiles-daemon.5.scd diff --git a/man/waybar-power-profiles-daemon.5.scd b/man/waybar-power-profiles-daemon.5.scd new file mode 100644 index 00000000..435fdbdf --- /dev/null +++ b/man/waybar-power-profiles-daemon.5.scd @@ -0,0 +1,72 @@ +waybar-power-profiles-daemon(5) + +# NAME + +waybar - power-profiles-daemon module + +# DESCRIPTION + +The *power-profiles-daemon* module displays the active power-profiles-daemon profile and cycle through the available profiles on click. + +# FILES + +$XDG_CONFIG_HOME/waybar/config + +# CONFIGURATION + + +[- *Option* +:- *Typeof* +:- *Default* +:= *Description* +|[ *format* +:[ string +:[ "{icon}" +:[ Message displayed on the bar. {icon} and {profile} are respectively substituted with the icon representing the active profile and its full name. +|[ *tooltip-format* +:[ string +:[ "Power profile: {profile}\\nDriver: {driver}" +:[ Messaged displayed in the module tooltip. {icon} and {profile} are respectively substituted with the icon representing the active profile and its full name. +|[ *tooltip* +:[ bool +:[ true +:[ Display the tooltip. +|[ *format-icons* +:[ object +:[ See default value in the example below. +:[ Icons used to represent the various power-profile. *Note*: the default configuration uses the font-awesome icons. You may want to override it if you don't have this font installed on your system. + + +# CONFIGURATION EXAMPLES + +Compact display (default config): + +``` +"power_profiles_daemon": { + "format": "{icon}", + "tooltip-format": "Power profile: {profile}\nDriver: {driver}", + "tooltip": true, + "format-icons": { + "default": "", + "performance": "", + "balanced": "", + "power-saver": "" + } +} +``` + +Display the full profile name: + +``` +"power_profiles_daemon": { + "format": "{icon} {profile}", + "tooltip-format": "Power profile: {profile}\nDriver: {driver}", + "tooltip": true, + "format-icons": { + "default": "", + "performance": "", + "balanced": "", + "power-saver": "" + } +} +``` diff --git a/meson.build b/meson.build index 10c82620..4ce7363d 100644 --- a/meson.build +++ b/meson.build @@ -222,6 +222,7 @@ if is_linux 'man/waybar-cpu.5.scd', 'man/waybar-memory.5.scd', 'man/waybar-systemd-failed-units.5.scd', + 'man/waybar-power-profiles-daemon.5.scd', ) elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd add_project_arguments('-DHAVE_CPU_BSD', language: 'cpp') @@ -578,4 +579,3 @@ if clangtidy.found() '-p', meson.project_build_root() ] + src_files) endif - From cc759a8b8f0d6c26d45093ff5586557e202751f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Fri, 1 Mar 2024 19:37:20 +0100 Subject: [PATCH 7/9] Power profiles daemon: address review comments Adding : - A missing try/catch - Glib::Error catch - Remove the useless destructor - Populate the profiles vector more efficiently - Numerous nits --- include/modules/power_profiles_daemon.hpp | 5 +- src/modules/power_profiles_daemon.cpp | 102 ++++++++++------------ 2 files changed, 48 insertions(+), 59 deletions(-) diff --git a/include/modules/power_profiles_daemon.hpp b/include/modules/power_profiles_daemon.hpp index bfb5c99d..edd9fe00 100644 --- a/include/modules/power_profiles_daemon.hpp +++ b/include/modules/power_profiles_daemon.hpp @@ -15,7 +15,6 @@ struct Profile { class PowerProfilesDaemon : public ALabel { public: PowerProfilesDaemon(const std::string &, const Json::Value &); - ~PowerProfilesDaemon() override; auto update() -> void override; void profileChangedCb(const Gio::DBus::Proxy::MapChangedProperties &, const std::vector &); @@ -38,12 +37,10 @@ class PowerProfilesDaemon : public ALabel { std::vector::iterator activeProfile_; // Current CSS class applied to the label std::string currentStyle_; - // Format strings - std::string labelFormat_; + // Format string std::string tooltipFormat_; // DBus Proxy used to track the current active profile Glib::RefPtr powerProfilesProxy_; - sigc::connection powerProfileChangeSignal_; }; } // namespace waybar::modules diff --git a/src/modules/power_profiles_daemon.cpp b/src/modules/power_profiles_daemon.cpp index bd6a52a7..ae3d7443 100644 --- a/src/modules/power_profiles_daemon.cpp +++ b/src/modules/power_profiles_daemon.cpp @@ -1,14 +1,6 @@ #include "modules/power_profiles_daemon.hpp" -// In the 80000 version of fmt library authors decided to optimize imports -// and moved declarations required for fmt::dynamic_format_arg_store in new -// header fmt/args.h -#if (FMT_VERSION >= 80000) #include -#else -#include -#endif - #include #include #include @@ -16,13 +8,7 @@ namespace waybar::modules { PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Value& config) - : ALabel(config, "power-profiles-daemon", id, "{profile}", 0, false, true), connected_(false) { - if (config_["format"].isString()) { - format_ = config_["format"].asString(); - } else { - format_ = "{icon}"; - } - + : ALabel(config, "power-profiles-daemon", id, "{icon}", 0, false, true), connected_(false) { if (config_["tooltip-format"].isString()) { tooltipFormat_ = config_["tooltip-format"].asString(); } else { @@ -58,27 +44,19 @@ PowerProfilesDaemon::PowerProfilesDaemon(const std::string& id, const Json::Valu sigc::mem_fun(*this, &PowerProfilesDaemon::busConnectedCb)); } -PowerProfilesDaemon::~PowerProfilesDaemon() { - if (powerProfileChangeSignal_.connected()) { - powerProfileChangeSignal_.disconnect(); - } - if (powerProfilesProxy_) { - powerProfilesProxy_.reset(); - } -} - void PowerProfilesDaemon::busConnectedCb(Glib::RefPtr& r) { try { powerProfilesProxy_ = Gio::DBus::Proxy::create_for_bus_finish(r); using GetAllProfilesVar = Glib::Variant>; auto callArgs = GetAllProfilesVar::create(std::make_tuple("net.hadess.PowerProfiles")); - - auto container = Glib::VariantBase::cast_dynamic(callArgs); powerProfilesProxy_->call("org.freedesktop.DBus.Properties.GetAll", - sigc::mem_fun(*this, &PowerProfilesDaemon::getAllPropsCb), container); + sigc::mem_fun(*this, &PowerProfilesDaemon::getAllPropsCb), callArgs); // Connect active profile callback } catch (const std::exception& e) { - spdlog::error("Failed to create the power profiles daemon DBus proxy"); + spdlog::error("Failed to create the power profiles daemon DBus proxy: {}", e.what()); + } catch (const Glib::Error& e) { + spdlog::error("Failed to create the power profiles daemon DBus proxy: {}", + std::string(e.what())); } } @@ -93,12 +71,14 @@ void PowerProfilesDaemon::getAllPropsCb(Glib::RefPtr& r) { // available, we can safely attach the activeProfile monitoring // now. connected_ = true; - powerProfileChangeSignal_ = powerProfilesProxy_->signal_properties_changed().connect( + powerProfilesProxy_->signal_properties_changed().connect( sigc::mem_fun(*this, &PowerProfilesDaemon::profileChangedCb)); populateInitState(); dp.emit(); } catch (const std::exception& err) { spdlog::error("Failed to query power-profiles-daemon via dbus: {}", err.what()); + } catch (const Glib::Error& err) { + spdlog::error("Failed to query power-profiles-daemon via dbus: {}", std::string(err.what())); } } @@ -111,18 +91,22 @@ void PowerProfilesDaemon::populateInitState() { using ProfilesType = std::vector>>; Glib::Variant profilesVariant; powerProfilesProxy_->get_cached_property(profilesVariant, "Profiles"); - Glib::ustring name; - Glib::ustring driver; - Profile profile; for (auto& variantDict : profilesVariant.get()) { + Glib::ustring name; + Glib::ustring driver; if (auto p = variantDict.find("Profile"); p != variantDict.end()) { name = p->second.get(); } if (auto d = variantDict.find("Driver"); d != variantDict.end()) { driver = d->second.get(); } - profile = {name, driver}; - availableProfiles_.push_back(profile); + if (!name.empty()) { + availableProfiles_.emplace_back(std::move(name), std::move(driver)); + } else { + spdlog::error( + "Power profiles daemon: power-profiles-daemon sent us an empty power profile name. " + "Something is wrong."); + } } // Find the index of the current activated mode (to toggle) @@ -157,12 +141,14 @@ 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"); + spdlog::error( + "Power profile daemon: can't find the active profile {} in the available profiles list", + str); } } auto PowerProfilesDaemon::update() -> void { - if (connected_) { + if (connected_ && activeProfile_ != availableProfiles_.end()) { auto profile = (*activeProfile_); // Set label fmt::dynamic_format_arg_store store; @@ -180,35 +166,41 @@ auto PowerProfilesDaemon::update() -> void { } label_.get_style_context()->add_class(profile.name); currentStyle_ = profile.name; - - ALabel::update(); + event_box_.set_visible(true); + } else { + event_box_.set_visible(false); } + + ALabel::update(); } bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { - if (connected_) { - if (e->type == GdkEventType::GDK_BUTTON_PRESS && powerProfilesProxy_) { - activeProfile_++; - if (activeProfile_ == availableProfiles_.end()) { - activeProfile_ = availableProfiles_.begin(); - } - - using VarStr = Glib::Variant; - using SetPowerProfileVar = Glib::Variant>; - VarStr activeProfileVariant = VarStr::create(activeProfile_->name); - auto callArgs = SetPowerProfileVar::create( - std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); - auto container = Glib::VariantBase::cast_dynamic(callArgs); - powerProfilesProxy_->call("org.freedesktop.DBus.Properties.Set", - sigc::mem_fun(*this, &PowerProfilesDaemon::setPropCb), container); + if (e->type == GdkEventType::GDK_BUTTON_PRESS && connected_) { + activeProfile_++; + if (activeProfile_ == availableProfiles_.end()) { + activeProfile_ = availableProfiles_.begin(); } + + using VarStr = Glib::Variant; + using SetPowerProfileVar = Glib::Variant>; + VarStr activeProfileVariant = VarStr::create(activeProfile_->name); + auto callArgs = SetPowerProfileVar::create( + std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); + powerProfilesProxy_->call("org.freedesktop.DBus.Properties.Set", + sigc::mem_fun(*this, &PowerProfilesDaemon::setPropCb), callArgs); } return true; } void PowerProfilesDaemon::setPropCb(Glib::RefPtr& r) { - auto _ = powerProfilesProxy_->call_finish(r); - update(); + try { + auto _ = powerProfilesProxy_->call_finish(r); + update(); + } catch (const std::exception& e) { + spdlog::error("Failed to set the the active power profile: {}", e.what()); + } catch (const Glib::Error& e) { + spdlog::error("Failed to set the active power profile: {}", std::string(e.what())); + } } } // namespace waybar::modules From 5ba7c9eb6046aec3f6f1fe747cd2b806e02b2218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Sat, 2 Mar 2024 09:40:19 +0100 Subject: [PATCH 8/9] modules/power-profiles-daemon: add some right padding The icon is not really centered in the box. This is likely coming from a bogus glyph width calculation. It's not a big deal, but that's not really pleasant aesthetically-wise. Adding a bit of right padding makes it much more pleasant to watch. It does not really disrupt a wider display form, like one that explicitely writes the active profile. --- resources/style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/style.css b/resources/style.css index 7f708ff4..b5859390 100644 --- a/resources/style.css +++ b/resources/style.css @@ -140,6 +140,10 @@ button:hover { animation-direction: alternate; } +#power-profiles-daemon { + padding-right: 15px; +} + #power-profiles-daemon.performance { background-color: #f53c3c; color: #ffffff; From 5578c122ab459c75e46d3029c5600eefc42bee03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Sat, 2 Mar 2024 18:37:32 +0100 Subject: [PATCH 9/9] modules/power-profiles-daemon: kebab case name in config power_profiles_daemon => power-profiles-daemon --- man/waybar-power-profiles-daemon.5.scd | 4 ++-- resources/config.jsonc | 4 ++-- src/factory.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/man/waybar-power-profiles-daemon.5.scd b/man/waybar-power-profiles-daemon.5.scd index 435fdbdf..82fad13b 100644 --- a/man/waybar-power-profiles-daemon.5.scd +++ b/man/waybar-power-profiles-daemon.5.scd @@ -42,7 +42,7 @@ $XDG_CONFIG_HOME/waybar/config Compact display (default config): ``` -"power_profiles_daemon": { +"power-profiles-daemon": { "format": "{icon}", "tooltip-format": "Power profile: {profile}\nDriver: {driver}", "tooltip": true, @@ -58,7 +58,7 @@ Compact display (default config): Display the full profile name: ``` -"power_profiles_daemon": { +"power-profiles-daemon": { "format": "{icon} {profile}", "tooltip-format": "Power profile: {profile}\nDriver: {driver}", "tooltip": true, diff --git a/resources/config.jsonc b/resources/config.jsonc index 4e300a33..58813925 100644 --- a/resources/config.jsonc +++ b/resources/config.jsonc @@ -20,7 +20,7 @@ "idle_inhibitor", "pulseaudio", "network", - "power_profiles_daemon", + "power-profiles-daemon", "cpu", "memory", "temperature", @@ -148,7 +148,7 @@ "battery#bat2": { "bat": "BAT2" }, - "power_profiles_daemon": { + "power-profiles-daemon": { "format": "{icon}", "tooltip-format": "Power profile: {profile}\nDriver: {driver}", "tooltip": true, diff --git a/src/factory.cpp b/src/factory.cpp index 7dc6709e..94076201 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -283,7 +283,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, if (ref == "bluetooth") { return new waybar::modules::Bluetooth(id, config_[name]); } - if (ref == "power_profiles_daemon") { + if (ref == "power-profiles-daemon") { return new waybar::modules::PowerProfilesDaemon(id, config_[name]); } #endif