Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions dev/PowerNotifications/PowerNotifications.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <PowerNotifications.h>
#include <frameworkudk\PowerNotificationsPal.h>
#include <Microsoft.Windows.System.Power.PowerManager.g.cpp>
#include <Microsoft.Windows.System.Power.SystemSuspendStatusChangedEventArgs.g.cpp>
#include <powrprof.h>

namespace winrt::Microsoft::Windows::System::Power::implementation
Expand Down Expand Up @@ -324,4 +325,19 @@ namespace winrt::Microsoft::Windows::System::Power::implementation
check_win32(PowerUnregisterSuspendResumeNotification(
Factory()->m_systemSuspendHandle));
}

SystemSuspendStatusChangedEventType& SystemSuspendStatus2_Event()
{
return Factory()->m_systemSuspendStatusChanged2Event;
}

void SystemSuspendStatus2_Register()
{
SystemSuspendStatus_Register();
}

void SystemSuspendStatus2_Unregister()
{
SystemSuspendStatus_Unregister();
}
}
121 changes: 113 additions & 8 deletions dev/PowerNotifications/PowerNotifications.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <mutex>
#include <powersetting.h>
#include <Microsoft.Windows.System.Power.PowerManager.g.h>
#include <Microsoft.Windows.System.Power.SystemSuspendStatusChangedEventArgs.g.h>
#include <frameworkudk\PowerNotificationsPal.h>
#include <WindowsAppRuntimeInsights.h>

Expand All @@ -27,11 +28,17 @@ namespace winrt::Microsoft::Windows::System::Power
using PowerEventHandler =
winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>;
using EventType = winrt::event<PowerEventHandler>;

using SystemSuspendStatusChangedEventHandler =
winrt::Windows::Foundation::TypedEventHandler<winrt::Windows::Foundation::IInspectable,
winrt::Microsoft::Windows::System::Power::SystemSuspendStatusChangedEventArgs>;
using SystemSuspendStatusChangedEventType = winrt::event<SystemSuspendStatusChangedEventHandler>;

// Forward-declarations
namespace implementation
{
struct PowerManager;
struct SystemSuspendStatusChangedEventArgs;

EventType& EnergySaverStatus_Event();
void EnergySaverStatus_Register();
Expand Down Expand Up @@ -86,6 +93,10 @@ namespace winrt::Microsoft::Windows::System::Power
void SystemSuspendStatus_Register();
void SystemSuspendStatus_Unregister();

SystemSuspendStatusChangedEventType& SystemSuspendStatus2_Event();
void SystemSuspendStatus2_Register();
void SystemSuspendStatus2_Unregister();

// A place holder for an empty function, since not all events have every function defined
void NoOperation() {}

Expand Down Expand Up @@ -135,6 +146,7 @@ namespace winrt::Microsoft::Windows::System::Power
EventType m_userPresenceStatusChangedEvent;
EventType m_systemAwayModeStatusChangedEvent;
EventType m_systemSuspendStatusChangedEvent;
SystemSuspendStatusChangedEventType m_systemSuspendStatusChanged2Event;

EnergySaverStatusRegistration m_energySaverStatusHandle{};
CompositeBatteryStatusRegistration m_batteryStatusHandle{};
Expand Down Expand Up @@ -228,6 +240,11 @@ namespace winrt::Microsoft::Windows::System::Power
return eventObj ? true : false;
}

bool RegisteredForEvents(const SystemSuspendStatusChangedEventType& eventObj)
{
return eventObj ? true : false;
}

event_token AddCallback(PowerFunctionDetails fn, const PowerEventHandler& handler)
{
try
Expand Down Expand Up @@ -607,39 +624,127 @@ namespace winrt::Microsoft::Windows::System::Power

event_token SystemSuspendStatusChanged(const PowerEventHandler& handler)
{
return AddCallback(systemSuspendFunc, handler);
try
{
PowerNotificationsTelemetry::AddCallbackTrace(L"SystemSuspendStatusChanged");
std::scoped_lock<std::mutex> lock(m_mutex);
// Register if neither event has listeners
if (!RegisteredForEvents(m_systemSuspendStatusChangedEvent) &&
!RegisteredForEvents(m_systemSuspendStatusChanged2Event))
{
SystemSuspendStatus_Register();
}
return m_systemSuspendStatusChangedEvent.add(handler);
}
catch (std::exception& ex)
{
PowerNotificationsTelemetry::FailureTrace(L"SystemSuspendStatusChanged", L"AddCallback", ex.what());
throw ex;
}
}

void SystemSuspendStatusChanged(const event_token& token)
{
RemoveCallback(systemSuspendFunc, token);
std::scoped_lock<std::mutex> lock(m_mutex);
m_systemSuspendStatusChangedEvent.remove(token);
// Unregister only if both events have no listeners
if (!RegisteredForEvents(m_systemSuspendStatusChangedEvent) &&
!RegisteredForEvents(m_systemSuspendStatusChanged2Event))
{
SystemSuspendStatus_Unregister();
}
}

event_token SystemSuspendStatusChanged2(const SystemSuspendStatusChangedEventHandler& handler)
{
try
{
PowerNotificationsTelemetry::AddCallbackTrace(L"SystemSuspendStatusChanged2");
std::scoped_lock<std::mutex> lock(m_mutex);
// Register if neither event has listeners
if (!RegisteredForEvents(m_systemSuspendStatusChangedEvent) &&
!RegisteredForEvents(m_systemSuspendStatusChanged2Event))
{
SystemSuspendStatus_Register();
}
return m_systemSuspendStatusChanged2Event.add(handler);
}
catch (std::exception& ex)
{
PowerNotificationsTelemetry::FailureTrace(L"SystemSuspendStatusChanged2", L"AddCallback", ex.what());
throw ex;
}
}

void SystemSuspendStatusChanged2(const event_token& token)
{
std::scoped_lock<std::mutex> lock(m_mutex);
m_systemSuspendStatusChanged2Event.remove(token);
// Unregister only if both events have no listeners
if (!RegisteredForEvents(m_systemSuspendStatusChangedEvent) &&
!RegisteredForEvents(m_systemSuspendStatusChanged2Event))
{
SystemSuspendStatus_Unregister();
}
}

winrt::fire_and_forget RaiseSystemSuspendStatusEvent(Power::SystemSuspendStatus status)
{
auto lifetime = get_strong();
co_await winrt::resume_background();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for having a look! If you are investigating adding a new event for this anyway, I’d highly recommend addressing this behavior as well:

It also fails to mention that the app can block the event handler for up to 2 seconds, delaying system suspend (as explained here). Or does that even work? When looking at the implementation, the event seems to be fired in a separate thread, making this non-functioning? https://github.com/microsoft/WindowsAppSDK/blob/main/dev/PowerNotifications/PowerNotifications.h#L266C26-L266C50

Instead of raising the new event on a background thread via co_await winrt::resume_background(), it should stay on the same thread the Win32 callback was called on. This ensures Windows correctly detects that the app is intentionally delaying suspend to finish suspend tasks, like flushing settings to disk.


// Raise old event (without args)
m_systemSuspendStatusChangedEvent(nullptr, nullptr);

// Raise new event (with args)
auto args = winrt::make<implementation::SystemSuspendStatusChangedEventArgs>(status);
m_systemSuspendStatusChanged2Event(nullptr, args);
}

void SystemSuspendStatusChanged_Callback(ULONG PowerEvent)
{
using namespace Power;
Power::SystemSuspendStatus newStatus = SystemSuspendStatus::Uninitialized;

if (PowerEvent == PBT_APMSUSPEND)
{
m_systemSuspendStatus = SystemSuspendStatus::Entering;
RaiseEvent(systemSuspendFunc);
newStatus = SystemSuspendStatus::Entering;
}
else if (PowerEvent == PBT_APMRESUMEAUTOMATIC)
{
m_systemSuspendStatus = SystemSuspendStatus::AutoResume;
RaiseEvent(systemSuspendFunc);
newStatus = SystemSuspendStatus::AutoResume;
}
else if (PowerEvent == PBT_APMRESUMESUSPEND)
{
m_systemSuspendStatus = SystemSuspendStatus::ManualResume;
RaiseEvent(systemSuspendFunc);
newStatus = SystemSuspendStatus::ManualResume;
}
else
{
return; // Unknown event, ignore
}

// Update the stored status for the property getter
m_systemSuspendStatus = newStatus;

// Raise both old and new events with the correct status
RaiseSystemSuspendStatusEvent(newStatus);
}

};
};

namespace implementation
{
struct SystemSuspendStatusChangedEventArgs : SystemSuspendStatusChangedEventArgsT<SystemSuspendStatusChangedEventArgs>
{
SystemSuspendStatusChangedEventArgs(Power::SystemSuspendStatus status) : m_status(status) {}

Power::SystemSuspendStatus Status() const { return m_status; }

private:
Power::SystemSuspendStatus m_status;
};

struct PowerManager
{
PowerManager() = delete;
Expand Down
11 changes: 11 additions & 0 deletions dev/PowerNotifications/PowerNotifications.idl
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ namespace Microsoft.Windows.System.Power
ManualResume
};

[contract(PowerNotificationsContract, 2)]
runtimeclass SystemSuspendStatusChangedEventArgs
{
SystemSuspendStatus Status{ get; };
}

[contract(PowerNotificationsContract, 1)]
static runtimeclass PowerManager
{
Expand Down Expand Up @@ -120,5 +126,10 @@ namespace Microsoft.Windows.System.Power

static SystemSuspendStatus SystemSuspendStatus{ get; };
static event Windows.Foundation.EventHandler<Object> SystemSuspendStatusChanged;

[contract(PowerNotificationsContract, 2)]
{
static event Windows.Foundation.TypedEventHandler<Object, SystemSuspendStatusChangedEventArgs> SystemSuspendStatusChanged2;
}
};
}
Loading
Loading