Skip to content
wxWidgets - Cross-Platform GUI Library
Stop Russia agression of Ukraine
Stop the War in Ukraine

wxWidgets is united with the people of Ukraine and the international community.

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!

Comments

Blog Archive