-
-
Notifications
You must be signed in to change notification settings - Fork 707
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce a power-profiles-daemon module #2971
Conversation
f999681
to
9502940
Compare
Fixes #1539 |
activeProfile_ = std::find_if(availableProfiles_.begin(), availableProfiles_.end(), pred); | ||
if (activeProfile_ == availableProfiles_.end()) { | ||
spdlog::error("PowerProfilesDaemon: FATAL, can't find the active profile in the available profiles list"); | ||
exit(-1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why an exit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Erf! Two reasons 😅
- When I wrote this, I did not know the best way to crash the module to prevent it to do some dangerous dereferences. I made a mental note about that and put this exit in the meantime.
- My memory is not so good, I tend to forget my mental notes very fast 😀
Anyway, after looking the current codebase, it seems like throwing a runtime error does the trick. Don't hesitate to point me to another direction if this is not the best way to go.
6cc362b
to
7405011
Compare
Rebased, there was a conflict on the config file. Documentation-wise, I could use some help. I'm not sure what to do for now. Is the wiki the canonical source for it? How are you dealing with it when adding a new feature like this? There's not much to document for now, the label ain't configurable: I personally don't really need that. I planned to implement that iteratively in a subsequent PR. Unless you'd prefer this to be done all at the same time in this PR.
|
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.
225f20f
to
f99f08b
Compare
When adding new feature like this, a new entry in the man and wiki with module settings like others is ok :) |
Thanks for the directions. I'll write the manpage section by the end of the week. (and fix the clang-tidy issues) I'm a bit puzzled by the freebsd CI failure. Is it expected? |
Thanks for implementing this, I've wanted it for a while! It would be nice to have the format string be configurable; the full profile names are a bit wide to fit neatly in my already crowded status bar, so I'd like to be able to use just 🌱 / ⚖️ / ⚡ for example. |
FreeBSD CI is flaky. It works via qemu vm in a macOS virtual machine, and there are random connectivity problems or timeouts. The actual status in the log is fine, so please ignore the failure.
I'd agree with that and even suggest to use the icons by default. For a module enabled in the default config, it takes too much screen space. |
f99f08b
to
162b41c
Compare
It appears that this PR crashes instantly if you don't have p-p-d installed. While investigating, I found a wonderful fact: So the module init should look like
...I think I need to schedule some time to review existing DBus modules. |
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.
2 changes to address the review feedback: 1. Aleksei pointed out in this comment (Alexays#2971 (comment)) 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.
Thanks for the detailed feedback @alebastr, it was super useful! From a (very) occasional C++ developper and Glib newbie perspective, this DBus API is pretty confusing and painful to use. :/ I adressed @lheckemann and @alebastr feedback wrt. the label formatting in 61fed6a. We're now only displaying a small icon in the default config. Everything, can be customized. I addressed #2971 (comment) in 901314a. I'll write the man page this EU afternoon. |
2 changes to address the review feedback: 1. Aleksei pointed out in this comment (Alexays#2971 (comment)) 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.
29d6eaa
to
b620eb9
Compare
The manpage is ready. PTAL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for addressing the DBus safety comments! Looks correct and works for me both with and without daemon.
I left a few more comments, mostly style nits and redundant code.
// 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Waybar requires fmt >= 8.1.1, this check is not necessary in a new code.
if (config_["format"].isString()) { | ||
format_ = config_["format"].asString(); | ||
} else { | ||
format_ = "{icon}"; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ALabel::ALabel
will do that for you if you pass the correct default format string to the constructor.
// Current CSS class applied to the label | ||
std::string currentStyle_; | ||
// Format strings | ||
std::string labelFormat_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
labelFormat_ is unused
if (powerProfileChangeSignal_.connected()) { | ||
powerProfileChangeSignal_.disconnect(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For any connections made with sigc::mem_fun
where the object inherits sigc::trackable
(base class for the modules does), this will happen automatically. You don't need to track or disconnect the connection.
powerProfileChangeSignal_.disconnect(); | ||
} | ||
if (powerProfilesProxy_) { | ||
powerProfilesProxy_.reset(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RefPtr
already does that during destruction.
} | ||
|
||
void PowerProfilesDaemon::setPropCb(Glib::RefPtr<Gio::AsyncResult>& r) { | ||
auto _ = powerProfilesProxy_->call_finish(r); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, check for exceptions here.
|
||
bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { | ||
if (connected_) { | ||
if (e->type == GdkEventType::GDK_BUTTON_PRESS && powerProfilesProxy_) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check for powerProfilesProxy_
could be redundant if you already check connected_
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"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure throwing exception is necessary here. It will be caught by Glibmm exception handler anyways, with no consequences.
You can just log the error and hide the module on the next update.
VarStr activeProfileVariant = VarStr::create(activeProfile_->name); | ||
auto callArgs = SetPowerProfileVar::create( | ||
std::make_tuple("net.hadess.PowerProfiles", "ActiveProfile", activeProfileVariant)); | ||
auto container = Glib::VariantBase::cast_dynamic<Glib::VariantContainerBase>(callArgs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The manual cast might be unnecessary
profile = {name, driver}; | ||
availableProfiles_.push_back(profile); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
availableProfiles_.emplace_back(name, driver);
would avoid one temporary object.
And if you move name/driver declaration into the loop (which you might want to do anyways to avoid accidentally reusing values from previous iterations), it'll be safe to write if (!name.empty()) { availableProfiles_.emplace_back(std::move(name), std::move(driver)); }
Thanks for having taken the time to write these detailed explanations. Learning a lot here :) Addressed these "nits" in the above commit. Don't hesitate to squash all these if you feel like it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Thanks for implementing this feature!
94b61c2
to
ee197e4
Compare
Loosely related note: there's a quite a few synchronous dbus calls throughout the codebase:
|
There was no way to display the default value of format-icons without breaking the table :(
ee197e4
to
42f442e
Compare
I just pushed a small visual fix. The fontawesome icons were not really centered in the box in the default config. I feel like there's something wrong with the calculation of the icon width in the typesetter code. Added a bit of padding-left to center everything. |
You also accidentally reverted the last set of the review fixes :) Would it make sense to use |
Yep, I did review all of those.
But here's a snippet that simulates a problem with gamemode service activation, blocks startup of waybar for the duration of a DBus timeout and then causes crash on invalid memory access. Fun. % mkdir -p ~/.local/share/dbus-1/services
% cat >~/.local/share/dbus-1/services/com.feralinteractive.GameMode.service <<EOF
[D-BUS Service]
Name=com.feralinteractive.GameMode
Exec=/usr/bin/sleep 30
EOF |
Adding : - A missing try/catch - Glib::Error catch - Remove the useless destructor - Populate the profiles vector more efficiently - Numerous nits
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.
42f442e
to
0153031
Compare
🤦 fixed
Yes, I also prefer that. The daemon itself is spelled using kebab cas. I originally used snake-case seeing it used for |
power_profiles_daemon => power-profiles-daemon
0153031
to
5578c12
Compare
LGTM, can you also update the github wiki? :) |
🎉 I created the module wiki page there: https://github.com/Alexays/Waybar/wiki/Module:-PowerProfilesDaemon |
@picnoir thanks a lot for this feature! Would it make sense to make it so scrolling up and down cycles up and down? Or right-click cycles down (to keep the symmetry with the current behavior)? |
Hey!
There's only 3 profiles available ATM. For my personal use case,
toggling through them is good enough.
If you feel like implementing this, you'll likely have to replace the
availableProfiles_ vector with something that can be iterated in both
directions (std::list?)
See: https://github.com/Alexays/Waybar/blob/2c927de4c624d0c560f73ad78b4abc85fb703562/include/modules/power_profiles_daemon.hpp#L35
Once that done, you'll need to retrieve which button has been pressed in
the handleToggle
(https://github.com/Alexays/Waybar/blob/2c927de4c624d0c560f73ad78b4abc85fb703562/src/modules/power_profiles_daemon.cpp#L177)
function. See the GdkEventButton documentation for more details
https://api.gtkd.org/gdk.c.types.GdkEventButton.html
Don't hesitate to ping me on Matrix/IRC or here if you're stuck and need
a helping hand!
|
Changing the container is not required, std::vector has a bidirectional iterator as well, I will make a pr for it. |
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:
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. I'm not 100% sure about myself on that one though :)
Demo video: https://social.alternativebit.fr/media/514a598a09c6c9047e61af405d188aa7b99a5730015c95ad787f7c023ea1f461.mp4