Developer Blog
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!
Hello Darkness
Posted on
wxWidgets 3.3.0 is not released yet, but there is no doubt about what will be the most important new feature in it when it does get released (hopefully some time soon): it will be support for dark mode in wxMSW, the Windows port of the library. This is “just” a cosmetic feature, but it’s also the most requested one since the entire history of wxWidgets existence, so it will be great to finally have it available, after all these years.
Of course, the delay hasn’t been just due to neglect of our users wishes. The main problem is that Microsoft still doesn’t officially support dark mode for the desktop Windows applications and so implementing it requires using undocumented API and also a huge amount of work on things not supported by those APIs, which are far from being complete.
I’m very grateful to KiCad organization for helping to solve the second part of the problem by funding my work on Windows dark mode implementation and, of course, also to all the other contributors who helped with reporting and fixing bugs. Thanks to them all, I believe that dark mode support is now in a good enough shape to be used in most applications, even though there are still some known problems, and, in fact, it’s already used in some of my own applications in production since quite some time.
However, due to the first problem, dark mode support is, and will remain, until Microsoft announces official support for it, disabled by default, and you will need to explicitly opt-in into using it. To give an example, this minimal wxWidgets application:
#include <wx/app.h>
#include <wx/artprov.h>
#include <wx/frame.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
class MyApp : public wxApp {
public:
bool OnInit() override {
// ❶ SetAppearance(Appearance::System);
auto f = new wxFrame(nullptr, wxID_ANY, "wxWidgets Example");
auto s = new wxGridSizer(1);
s->Add(new wxStaticText(f, wxID_ANY, "Hello, world!"), wxSizerFlags().Center());
f->SetSizer(s);
f->SetIcon(wxArtProvider::GetIcon(wxART_WX_LOGO));
f->Show();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
still has the same boring, old, tired appearance, even when the system uses
dark mode by default, shown on the left below. However by uncommenting the
line marked with ❶ in the beginning of OnInit()
the appearance changes to
the modern, new, wired look on the right:
Of course, this assumes that the system uses the dark look by default. If it
doesn’t, the appearance would naturally stay the same, even with the
SetAppearance()
call. As the documentation explains,
this function can also be used with Appearance::Dark
argument to force the
use of dark mode or, to emphasize that dark mode is not wanted, with
Appearance::Light
.
Also note that while calling SetAppearance()
is required for dark mode
support under Windows, the situation under other platforms is a bit
different: because they do provide perfectly official and safe to use API for
the desktop applications, the system mode is used there by default and calling
SetAppearance(Appearance::System)
doesn’t do anything there – however it
does no harm neither and using this function to force either dark or light
appearance works under the other platforms as expected too.
Finally, there is a semi-secret way of enabling dark mode support for Windows
applications even if they don’t call SetAppearance()
: setting the
environment variable wx_msw_dark_mode
to the value of 1 has the same effect
as opting in into using the system appearance, while setting it to 2 forces
the use of dark appearance. However, if the application does set its own
appearance, this overrides the environment variable value, so this trick is
really only supposed to be used for the older applications which haven’t been
updated to call SetAppearance()
yet.
I’d like to end this post by repeating that everything described above is available in current Git master and while dark mode support will be included in wxWidgets 3.3.0 release, you are also more than welcome to try it already and report (and maybe help fix?) any problems you find!
Survival of the Oldest
Posted on
I’ve accidentally learnt about the Git of Theseus tool today and, of course, couldn’t resist running it on wxWidgets repository. After spinning the fans for quite some time, it finished analysing it and I could create a graph showing the survival rate of the lines of code in our code:
The exponential fit here is extremely bad, which is not really surprising considering that wxWidgets consists of several parts, evolving (or stagnating) at their own rate, but it’s still interesting to see that the half-life of more than 6 years is at the very end of the range of values presented in the table at the end of the tool’s author blog post, with only Git itself (at 6.60 years) having a value significantly higher than this.
Another potentially interesting graph is that of code added by year:
which clearly shows when old ports were removed from the repository (all those cliffs) but also the most productive years in terms of code added — and that the recent years are not among them, unfortunately.
Hopefully this will change when somebody contributes GTK 4 support!