Linux Power
Posted on
This post is (unfortunately) not about secret Linux power features, but just
about handling wxPowerEvent with wxGTK under Linux. Until recently, such
events were only generated in wxMSW, but now that the corresponding PR
has been merged, wxEVT_POWER_SUSPENDED
and wxEVT_POWER_RESUME
can be
generated when using wxGTK as well – however there is a small twist. Unlike
under Microsoft Windows, they are not generated by default because trying to
do anything in wxEVT_POWER_SUSPENDED
handler could fail due to a race
condition: the system might go to sleep before the handler finished its
execution. To prevent this from happening, the usual pattern is to acquire a
power lock, perform the required action when suspension notification is
received and then release the lock to let the system suspend.
So, in an attempt to be a good Linux desktop citizen, wxGTK doesn’t generate
these events until a wxPowerResourceBlocker object is created. However,
because the intention here is not to prevent the system from sleeping, but
just delay it for sufficiently long to allow the application event handler to
run, it should be created with the (new) wxPOWER_DELAY
block kind instead
of the default wxPOWER_PREVENT
one, e.g. like this:
wxPowerResourceBlocker delaySleep(wxPOWER_RESOURCE_SYSTEM,
_("Close open files"),
wxPOWER_DELAY)
The provided string is used to explain to the user why is sleep being delayed and appears e.g. in the output of systemd-inhibit tool.
Also, typically this variable would be a member variable of the application or main window class and not a local variable, so a program that wants to handle suspend/resume notifications should look like this:
#include <wx/wx.h>
class MyFrame : public wxFrame {
public:
MyFrame() :
wxFrame(nullptr, wxID_ANY, "My Power"),
m_delaySleep(wxPOWER_RESOURCE_SYSTEM, _("Save changes"), wxPOWER_DELAY)
{
Bind(wxEVT_POWER_SUSPENDED, [this](wxPowerEvent&) {
... save unsaved modifications to some temporary file ...
});
Bind(wxEVT_POWER_RESUME, [this](wxPowerEvent&) {
... restore modifications from the file, if it exists ...
});
...
}
private:
wxPowerResourceBlocker m_delaySleep;
};
class MyApp : public wxApp {
public:
bool OnInit() override { auto* f = new MyFrame(); f->Show(); }
};
This will also work in wxMSW, but there it would work even without
m_delaySleep
while you need to create it when using wxGTK. And, of course,
this requires some minimum system support under Linux, but it should work at
least on all contemporary desktop systems using systemd.
Unfortunately power events are still not generated under macOS so, as always, further enhancements are still possible – and would be very welcome!