mirror of
https://github.com/wxWidgets/wxWidgets.git
synced 2026-08-17 17:02:51 +08:00
Add support for wxEVT_POWER_{SUSPENDED,RESUME} under Linux
Generate these events on the systems running systemd, which provides org.freedesktop.login1 D-Bus interface, but require acquiring system power resource with the special new wxPOWER_DELAY "block mode" in order to do it. This means that existing applications need to be updated in order to start getting these events, but it has the advantage of avoiding to have to open the D-Bus connection on startup of every program, even if it's never going to use it, and also forces the program to provide the explanatory message, which is nice to have as it is shown by tools such as systemd-inhibit to show what could be preventing system sleep. Closes #23717.
This commit is contained in:
+11
-3
@@ -104,11 +104,18 @@ enum wxPowerResourceKind
|
||||
wxPOWER_RESOURCE_SYSTEM
|
||||
};
|
||||
|
||||
enum wxPowerBlockKind
|
||||
{
|
||||
wxPOWER_PREVENT,
|
||||
wxPOWER_DELAY
|
||||
};
|
||||
|
||||
class WXDLLIMPEXP_BASE wxPowerResource
|
||||
{
|
||||
public:
|
||||
static bool Acquire(wxPowerResourceKind kind,
|
||||
const wxString& reason = wxString());
|
||||
const wxString& reason = wxString(),
|
||||
wxPowerBlockKind blockKind = wxPOWER_PREVENT);
|
||||
static void Release(wxPowerResourceKind kind);
|
||||
};
|
||||
|
||||
@@ -116,9 +123,10 @@ class wxPowerResourceBlocker
|
||||
{
|
||||
public:
|
||||
explicit wxPowerResourceBlocker(wxPowerResourceKind kind,
|
||||
const wxString& reason = wxString())
|
||||
const wxString& reason = wxString(),
|
||||
wxPowerBlockKind blockKind = wxPOWER_PREVENT)
|
||||
: m_kind(kind),
|
||||
m_acquired(wxPowerResource::Acquire(kind, reason))
|
||||
m_acquired(wxPowerResource::Acquire(kind, reason, blockKind))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+85
-10
@@ -35,6 +35,31 @@ enum wxPowerResourceKind
|
||||
wxPOWER_RESOURCE_SYSTEM
|
||||
};
|
||||
|
||||
/**
|
||||
Possible blocking behaviours for power resources.
|
||||
|
||||
@since 3.3.0
|
||||
*/
|
||||
enum wxPowerBlockKind
|
||||
{
|
||||
/**
|
||||
Prevent the resource from disappearing.
|
||||
|
||||
When combined with ::wxPOWER_RESOURCE_SCREEN, this prevents the screen
|
||||
from turning off automatically. When combined with
|
||||
::wxPOWER_RESOURCE_SYSTEM, this prevents the system from suspending.
|
||||
*/
|
||||
wxPOWER_PREVENT,
|
||||
|
||||
/**
|
||||
Delay suspend until the application handles the corresponding event.
|
||||
|
||||
This block kind can only be used with ::wxPOWER_RESOURCE_SYSTEM.
|
||||
*/
|
||||
wxPOWER_DELAY
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@class wxPowerEvent
|
||||
|
||||
@@ -42,12 +67,45 @@ enum wxPowerResourceKind
|
||||
system is suspended, hibernated, plugged into or unplugged from the wall socket
|
||||
and so on. wxPowerEvents are emitted by wxWindows.
|
||||
|
||||
Notice that currently only suspend and resume events are generated and only
|
||||
under MS Windows platform. To avoid the need to change the code using this
|
||||
event later when these events are implemented on the other platforms please
|
||||
use the test <tt>ifdef wxHAS_POWER_EVENTS</tt> instead of directly testing for
|
||||
the platform in your code: this symbol will be defined for all platforms
|
||||
supporting the power events.
|
||||
Notice that currently these events are generated only under MSW and Linux
|
||||
and that under Linux you @e must use wxPowerResource::Acquire() to receive
|
||||
them, e.g. typically an application interested in these events should
|
||||
initialize wxPowerResourceBlocker for ::wxPOWER_RESOURCE_SYSTEM resource
|
||||
using ::wxPOWER_DELAY block kind when initializing either the application
|
||||
itself or its main window. E.g. for an application that wants to handle
|
||||
suspend to gracefully close the open network connections and then reopen
|
||||
them on resume you could do
|
||||
|
||||
@code
|
||||
class MyMainFrame : public wxFrame
|
||||
{
|
||||
...
|
||||
|
||||
private:
|
||||
wxPowerResourceBlocker m_powerDelaySleep;
|
||||
};
|
||||
|
||||
MyMainFrame::MyMainFrame()
|
||||
: wxFrame(...),
|
||||
m_powerDelaySleep(wxPOWER_RESOURCE_SYSTEM,
|
||||
"MyApp needs to close network connections",
|
||||
wxPOWER_DELAY)
|
||||
{
|
||||
Bind(wxEVT_POWER_SUSPENDED, [](wxPowerEvent&) {
|
||||
// Close network connections here.
|
||||
});
|
||||
|
||||
Bind(wxEVT_POWER_RESUME, [](wxPowerEvent&) {
|
||||
// Restore previously closed network connections here.
|
||||
});
|
||||
}
|
||||
@endcode
|
||||
|
||||
To avoid the need to change the code using this event later when these
|
||||
events are implemented on the other platforms please use the test <tt>ifdef
|
||||
wxHAS_POWER_EVENTS</tt> instead of directly testing for the platform in
|
||||
your code: this symbol will be defined for all platforms supporting the
|
||||
power events.
|
||||
|
||||
@beginEventTable{wxPowerEvent}
|
||||
@event{EVT_POWER_SUSPENDING(func)}
|
||||
@@ -64,6 +122,8 @@ enum wxPowerResourceKind
|
||||
connections here, possibly remembering them to reopen them later when
|
||||
the system is resumed.
|
||||
@event{EVT_POWER_SUSPEND_CANCEL(func)}
|
||||
@warning This event is currently never generated.
|
||||
|
||||
System suspension was cancelled because some application vetoed it.
|
||||
@event{EVT_POWER_RESUME(func)}
|
||||
System resumed from suspend: normally the application should restore
|
||||
@@ -125,8 +185,16 @@ public:
|
||||
/**
|
||||
Acquire a power resource for the application.
|
||||
|
||||
If successful, the system will not automatically power of the screen or
|
||||
suspend until Release() is called.
|
||||
The default behaviour, chosen by setting @a blockKind to
|
||||
::wxPOWER_PREVENT, is to ensure that the resource of the corresponding
|
||||
@a kind remains accessible, i.e. prevent the system from turning off the
|
||||
screen or suspending.
|
||||
|
||||
Specifying ::wxPOWER_DELAY for @a blockKind doesn't actually prevent the
|
||||
system from suspending but does delay it to allow the application to
|
||||
handle the corresponding notifications. Note that under Linux systems
|
||||
will *not* receive ::wxEVT_POWER_SUSPENDED without acquiring the system
|
||||
resource using ::wxPOWER_DELAY.
|
||||
|
||||
Every call to Acquire @b must be matched by a corresponding call to
|
||||
Release() or the system will not suspend until the application ends, use
|
||||
@@ -138,12 +206,18 @@ public:
|
||||
some platforms to inform the user what is preventing power saving.
|
||||
It should usually describe the operation requiring the resource and
|
||||
specifying it is strongly recommended.
|
||||
@param blockKind The default value corresponds to the blocking
|
||||
behaviour, the ::wxPOWER_DELAY value can be used to avoid blocking
|
||||
the resource but just delay it to allow the application to handle
|
||||
the corresponding notifications. This parameter is available in
|
||||
wxWidgets 3.3.0 and later.
|
||||
@return Returns true if the acquisition was successful.
|
||||
|
||||
@see Release()
|
||||
*/
|
||||
static bool Acquire(wxPowerResourceKind kind,
|
||||
const wxString& reason = wxString());
|
||||
const wxString& reason = wxString(),
|
||||
wxPowerBlockKind blockKind = wxPOWER_PREVENT);
|
||||
|
||||
/**
|
||||
Release a previously acquired power resource.
|
||||
@@ -203,7 +277,8 @@ public:
|
||||
Uses the same parameters as wxPowerResource::Acquire().
|
||||
*/
|
||||
explicit wxPowerResourceBlocker(wxPowerResourceKind kind,
|
||||
const wxString& reason = wxString());
|
||||
const wxString& reason = wxString(),
|
||||
wxPowerBlockKind blockKind = wxPOWER_PREVENT);
|
||||
|
||||
/**
|
||||
Returns whether the power resource could be acquired.
|
||||
|
||||
@@ -263,12 +263,22 @@ wxEND_EVENT_TABLE()
|
||||
class MyApp : public wxApp
|
||||
{
|
||||
public:
|
||||
MyApp()
|
||||
: m_powerDelaySleep(wxPOWER_RESOURCE_SYSTEM,
|
||||
"Sample needs to show sleep event",
|
||||
wxPOWER_DELAY)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
new MyFrame;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
wxPowerResourceBlocker m_powerDelaySleep;
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(MyApp);
|
||||
|
||||
@@ -47,7 +47,8 @@
|
||||
|
||||
bool
|
||||
wxPowerResource::Acquire(wxPowerResourceKind WXUNUSED(kind),
|
||||
const wxString& WXUNUSED(reason))
|
||||
const wxString& WXUNUSED(reason),
|
||||
wxPowerBlockKind WXUNUSED(blockKind))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
+102
-13
@@ -28,6 +28,7 @@
|
||||
#include "wx/intl.h"
|
||||
#include "wx/log.h"
|
||||
#include "wx/module.h"
|
||||
#include "wx/window.h"
|
||||
#endif
|
||||
|
||||
#include "wx/power.h"
|
||||
@@ -71,7 +72,12 @@ public:
|
||||
close(m_fdInhibit);
|
||||
}
|
||||
|
||||
bool Inhibit(const wxString& reason);
|
||||
// Start or stop blocking system sleep/shutdown.
|
||||
bool StartInhibit(const wxString& reason, wxPowerBlockKind block);
|
||||
void StopInhibit();
|
||||
|
||||
// Resume blocking with the same parameters as before.
|
||||
void RestartInhibit();
|
||||
|
||||
private:
|
||||
static GDBusProxy* CreateProxyLoginManager();
|
||||
@@ -80,10 +86,14 @@ private:
|
||||
|
||||
int m_fdInhibit = INVALID_FD;
|
||||
|
||||
// Arguments passed to the last successful call to StartInhibit().
|
||||
wxString m_reason;
|
||||
wxPowerBlockKind m_blockKind;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxGDBusLoginManagerProxy);
|
||||
};
|
||||
|
||||
// The global login manager proxy, non-null if g_powerResourceAcquired != 0.
|
||||
// The global login manager proxy.
|
||||
std::unique_ptr<wxGDBusLoginManagerProxy> g_proxyLoginManager;
|
||||
|
||||
// Net number of times system power resource was acquired.
|
||||
@@ -119,6 +129,43 @@ private:
|
||||
// real implementation
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
extern "C" {
|
||||
static void
|
||||
wx_dbus_login_manager_cb(
|
||||
GDBusProxy* WXUNUSED(proxy),
|
||||
const gchar* WXUNUSED(sender),
|
||||
const gchar* signalname,
|
||||
GVariant* args,
|
||||
gpointer WXUNUSED(data)
|
||||
)
|
||||
{
|
||||
// We're only interested in a single signal.
|
||||
if ( strcmp(signalname, "PrepareForSleep") != 0 )
|
||||
return;
|
||||
|
||||
gboolean suspending = FALSE;
|
||||
g_variant_get(args, "(b)", &suspending);
|
||||
|
||||
wxPowerEvent event(suspending ? wxEVT_POWER_SUSPENDED : wxEVT_POWER_RESUME);
|
||||
for ( auto tlw : wxTopLevelWindows )
|
||||
{
|
||||
if ( tlw->IsShown() )
|
||||
tlw->HandleWindowEvent(event);
|
||||
}
|
||||
|
||||
if ( g_powerResourceAcquired )
|
||||
{
|
||||
// If we're suspending, we need to release the power resource to allow
|
||||
// suspend to go ahead without waiting for the timeout -- and then we
|
||||
// need to take it back again when we resume.
|
||||
if ( suspending )
|
||||
g_proxyLoginManager->StopInhibit();
|
||||
else
|
||||
g_proxyLoginManager->RestartInhibit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
GDBusProxy* wxGDBusLoginManagerProxy::CreateProxyLoginManager()
|
||||
{
|
||||
@@ -142,11 +189,15 @@ GDBusProxy* wxGDBusLoginManagerProxy::CreateProxyLoginManager()
|
||||
);
|
||||
}
|
||||
|
||||
g_signal_connect(proxyLoginManager, "g-signal",
|
||||
G_CALLBACK(wx_dbus_login_manager_cb), nullptr);
|
||||
|
||||
return proxyLoginManager;
|
||||
}
|
||||
|
||||
// Start inhibiting system suspend.
|
||||
bool wxGDBusLoginManagerProxy::Inhibit(const wxString& reason)
|
||||
bool
|
||||
wxGDBusLoginManagerProxy::StartInhibit(const wxString& reason,
|
||||
wxPowerBlockKind blockKind)
|
||||
{
|
||||
if ( !m_proxyLoginManager )
|
||||
return false;
|
||||
@@ -163,8 +214,24 @@ bool wxGDBusLoginManagerProxy::Inhibit(const wxString& reason)
|
||||
|
||||
// And also some user-readable reason for doing this.
|
||||
wxString why = reason;
|
||||
if ( why.empty() )
|
||||
why = _("Application needs to keep running");
|
||||
const char* what = nullptr;
|
||||
const char* mode = nullptr;
|
||||
switch ( blockKind )
|
||||
{
|
||||
case wxPOWER_PREVENT:
|
||||
mode = "block";
|
||||
what = "sleep:shutdown:idle";
|
||||
if ( why.empty() )
|
||||
why = _("Application needs to keep running");
|
||||
break;
|
||||
|
||||
case wxPOWER_DELAY:
|
||||
mode = "delay";
|
||||
what = "sleep";
|
||||
if ( why.empty() )
|
||||
why = _("Clean up before suspend");
|
||||
break;
|
||||
}
|
||||
|
||||
wxGtkObject<GUnixFDList> fd_list;
|
||||
wxGtkError error;
|
||||
@@ -173,10 +240,10 @@ bool wxGDBusLoginManagerProxy::Inhibit(const wxString& reason)
|
||||
"Inhibit",
|
||||
g_variant_new(
|
||||
"(ssss)",
|
||||
"sleep:shutdown:idle",
|
||||
what,
|
||||
static_cast<const char*>(who.utf8_str()),
|
||||
static_cast<const char*>(why.utf8_str()),
|
||||
"block"
|
||||
mode
|
||||
),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
G_MAXINT, // No timeout.
|
||||
@@ -202,15 +269,36 @@ bool wxGDBusLoginManagerProxy::Inhibit(const wxString& reason)
|
||||
m_fdInhibit = fds[0];
|
||||
g_free(fds);
|
||||
|
||||
// Remember them so that we could call Inhibit() with the same arguments
|
||||
// again later from RestartInhibit().
|
||||
m_reason = reason;
|
||||
m_blockKind = blockKind;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void wxGDBusLoginManagerProxy::StopInhibit()
|
||||
{
|
||||
wxCHECK_RET( m_fdInhibit != INVALID_FD, "Not inhibited" );
|
||||
|
||||
close(m_fdInhibit);
|
||||
|
||||
m_fdInhibit = INVALID_FD;
|
||||
}
|
||||
|
||||
void wxGDBusLoginManagerProxy::RestartInhibit()
|
||||
{
|
||||
StartInhibit(m_reason, m_blockKind);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// public API
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
wxPowerResource::Acquire(wxPowerResourceKind kind, const wxString& reason)
|
||||
wxPowerResource::Acquire(wxPowerResourceKind kind,
|
||||
const wxString& reason,
|
||||
wxPowerBlockKind blockKind)
|
||||
{
|
||||
switch ( kind )
|
||||
{
|
||||
@@ -221,11 +309,12 @@ wxPowerResource::Acquire(wxPowerResourceKind kind, const wxString& reason)
|
||||
case wxPOWER_RESOURCE_SYSTEM:
|
||||
if ( !g_powerResourceAcquired++ )
|
||||
{
|
||||
g_proxyLoginManager.reset(new wxGDBusLoginManagerProxy);
|
||||
if ( g_proxyLoginManager->Inhibit(reason) )
|
||||
if ( !g_proxyLoginManager )
|
||||
g_proxyLoginManager.reset(new wxGDBusLoginManagerProxy);
|
||||
|
||||
if ( g_proxyLoginManager->StartInhibit(reason, blockKind) )
|
||||
return true;
|
||||
|
||||
g_proxyLoginManager.reset();
|
||||
g_powerResourceAcquired--;
|
||||
}
|
||||
break;
|
||||
@@ -243,7 +332,7 @@ void wxPowerResource::Release(wxPowerResourceKind kind)
|
||||
|
||||
case wxPOWER_RESOURCE_SYSTEM:
|
||||
if ( !--g_powerResourceAcquired )
|
||||
g_proxyLoginManager.reset();
|
||||
g_proxyLoginManager->StopInhibit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-1
@@ -58,8 +58,15 @@ bool UpdatePowerResourceExecutionState()
|
||||
|
||||
bool
|
||||
wxPowerResource::Acquire(wxPowerResourceKind kind,
|
||||
const wxString& WXUNUSED(reason))
|
||||
const wxString& WXUNUSED(reason),
|
||||
wxPowerBlockKind blockKind)
|
||||
{
|
||||
if ( blockKind == wxPOWER_DELAY )
|
||||
{
|
||||
// We don't support this mode under MSW because it's not needed there.
|
||||
return true;
|
||||
}
|
||||
|
||||
switch ( kind )
|
||||
{
|
||||
case wxPOWER_RESOURCE_SCREEN:
|
||||
|
||||
+10
-1
@@ -68,8 +68,17 @@ bool UpdatePowerResourceUsage(wxPowerResourceKind kind, const wxString& reason)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wxPowerResource::Acquire(wxPowerResourceKind kind, const wxString& reason)
|
||||
bool
|
||||
wxPowerResource::Acquire(wxPowerResourceKind kind,
|
||||
const wxString& reason,
|
||||
wxPowerBlockKind blockKind)
|
||||
{
|
||||
if ( blockKind == wxPOWER_DELAY )
|
||||
{
|
||||
// We don't support this mode under macOS because it's not needed there.
|
||||
return true;
|
||||
}
|
||||
|
||||
wxAtomicInc(g_powerResourceSystemRefCount);
|
||||
|
||||
bool success = UpdatePowerResourceUsage(kind, reason);
|
||||
|
||||
Reference in New Issue
Block a user