mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Use sleep mode listener to fix modern standby sleep mode issue
This commit is contained in:
parent
d56d85b702
commit
f239866c68
3 changed files with 150 additions and 14 deletions
|
|
@ -85,5 +85,10 @@ QueryFullProcessImageName
|
|||
EVENT_OBJECT_HIDE
|
||||
EVENT_SYSTEM_DIALOGEND
|
||||
|
||||
DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS
|
||||
WM_POWERBROADCAST
|
||||
PBT_APMRESUMEAUTOMATIC
|
||||
PBT_APMRESUMEAUTOMATIC
|
||||
PBT_APMRESUMESUSPEND
|
||||
PowerRegisterSuspendResumeNotification
|
||||
PowerUnregisterSuspendResumeNotification
|
||||
DeviceNotifyCallbackRoutine
|
||||
|
|
@ -19,6 +19,7 @@ using Microsoft.Win32.SafeHandles;
|
|||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Graphics.Dwm;
|
||||
using Windows.Win32.System.Power;
|
||||
using Windows.Win32.System.Threading;
|
||||
using Windows.Win32.UI.Input.KeyboardAndMouse;
|
||||
using Windows.Win32.UI.Shell.Common;
|
||||
|
|
@ -338,9 +339,6 @@ namespace Flow.Launcher.Infrastructure
|
|||
public const int SC_MAXIMIZE = (int)PInvoke.SC_MAXIMIZE;
|
||||
public const int SC_MINIMIZE = (int)PInvoke.SC_MINIMIZE;
|
||||
|
||||
public const int WM_POWERBROADCAST = (int)PInvoke.WM_POWERBROADCAST;
|
||||
public const int PBT_APMRESUMEAUTOMATIC = (int)PInvoke.PBT_APMRESUMEAUTOMATIC;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Window Handle
|
||||
|
|
@ -918,5 +916,105 @@ namespace Flow.Launcher.Infrastructure
|
|||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Sleep Mode Listener
|
||||
|
||||
private static Action _func;
|
||||
private static PDEVICE_NOTIFY_CALLBACK_ROUTINE _callback = null;
|
||||
private static DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS _recipient;
|
||||
private static SafeHandle _recipientHandle;
|
||||
private static HPOWERNOTIFY _handle = HPOWERNOTIFY.Null;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a listener for sleep mode events.
|
||||
/// Inspired from: https://github.com/XKaguya/LenovoLegionToolkit
|
||||
/// https://blog.csdn.net/mochounv/article/details/114668594
|
||||
/// </summary>
|
||||
/// <param name="func"></param>
|
||||
/// <exception cref="Win32Exception"></exception>
|
||||
public static unsafe void RegisterSleepModeListener(Action func)
|
||||
{
|
||||
if (_callback != null)
|
||||
{
|
||||
// Only register if not already registered
|
||||
return;
|
||||
}
|
||||
|
||||
_func = func;
|
||||
_callback = new PDEVICE_NOTIFY_CALLBACK_ROUTINE(DeviceNotifyCallback);
|
||||
_recipient = new DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS()
|
||||
{
|
||||
Callback = _callback,
|
||||
Context = null
|
||||
};
|
||||
|
||||
_recipientHandle = new StructSafeHandle<DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS>(_recipient);
|
||||
_handle = PInvoke.PowerRegisterSuspendResumeNotification(
|
||||
REGISTER_NOTIFICATION_FLAGS.DEVICE_NOTIFY_CALLBACK,
|
||||
_recipientHandle,
|
||||
out var handle) == WIN32_ERROR.ERROR_SUCCESS ?
|
||||
new HPOWERNOTIFY(new IntPtr(handle)) :
|
||||
HPOWERNOTIFY.Null;
|
||||
if (_handle.IsNull)
|
||||
{
|
||||
throw new Win32Exception("Error registering for power notifications: " + Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the sleep mode listener.
|
||||
/// </summary>
|
||||
public static void UnregisterSleepModeListener()
|
||||
{
|
||||
if (!_handle.IsNull)
|
||||
{
|
||||
PInvoke.PowerUnregisterSuspendResumeNotification(_handle);
|
||||
_handle = HPOWERNOTIFY.Null;
|
||||
_func = null;
|
||||
_callback = null;
|
||||
_recipientHandle = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe uint DeviceNotifyCallback(void* context, uint type, void* setting)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PInvoke.PBT_APMRESUMEAUTOMATIC:
|
||||
// Operation is resuming automatically from a low-power state.This message is sent every time the system resumes
|
||||
_func();
|
||||
break;
|
||||
|
||||
case PInvoke.PBT_APMRESUMESUSPEND:
|
||||
// Operation is resuming from a low-power state.This message is sent after PBT_APMRESUMEAUTOMATIC if the resume is triggered by user input, such as pressing a key
|
||||
_func();
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private sealed class StructSafeHandle<T> : SafeHandle where T : struct
|
||||
{
|
||||
private readonly nint _ptr = nint.Zero;
|
||||
|
||||
public StructSafeHandle(T recipient) : base(nint.Zero, true)
|
||||
{
|
||||
var pRecipient = Marshal.AllocHGlobal(Marshal.SizeOf<T>());
|
||||
Marshal.StructureToPtr(recipient, pRecipient, false);
|
||||
SetHandle(pRecipient);
|
||||
_ptr = pRecipient;
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == nint.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Marshal.FreeHGlobal(_ptr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ namespace Flow.Launcher
|
|||
UpdatePosition();
|
||||
|
||||
InitSoundEffects();
|
||||
RegisterSoundEffectsEvent();
|
||||
DataObject.AddPastingHandler(QueryTextBox, QueryTextBox_OnPaste);
|
||||
_viewModel.ActualApplicationThemeChanged += ViewModel_ActualApplicationThemeChanged;
|
||||
}
|
||||
|
|
@ -668,16 +669,6 @@ namespace Flow.Launcher
|
|||
handled = true;
|
||||
}
|
||||
break;
|
||||
case Win32Helper.WM_POWERBROADCAST: // Handle power broadcast messages
|
||||
// https://learn.microsoft.com/en-us/windows/win32/power/wm-powerbroadcast
|
||||
if (wParam.ToInt32() == Win32Helper.PBT_APMRESUMEAUTOMATIC)
|
||||
{
|
||||
// Fix for sound not playing after sleep / hibernate
|
||||
// https://stackoverflow.com/questions/64805186/mediaplayer-doesnt-play-after-computer-sleeps
|
||||
InitSoundEffects();
|
||||
}
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return IntPtr.Zero;
|
||||
|
|
@ -723,6 +714,47 @@ namespace Flow.Launcher
|
|||
}
|
||||
}
|
||||
|
||||
private void RegisterSoundEffectsEvent()
|
||||
{
|
||||
// Fix for sound not playing after sleep / hibernate for both modern standby and legacy standby
|
||||
// https://stackoverflow.com/questions/64805186/mediaplayer-doesnt-play-after-computer-sleeps
|
||||
try
|
||||
{
|
||||
Win32Helper.RegisterSleepModeListener(() =>
|
||||
{
|
||||
if (Application.Current == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We must run InitSoundEffects on UI thread because MediaPlayer is a DispatcherObject
|
||||
if (!Application.Current.Dispatcher.CheckAccess())
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(InitSoundEffects);
|
||||
return;
|
||||
}
|
||||
|
||||
InitSoundEffects();
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.API.LogException(ClassName, "Failed to register sound effect event", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void UnregisterSoundEffectsEvent()
|
||||
{
|
||||
try
|
||||
{
|
||||
Win32Helper.UnregisterSleepModeListener();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.API.LogException(ClassName, "Failed to unregister sound effect event", e);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Window Notify Icon
|
||||
|
|
@ -1447,6 +1479,7 @@ namespace Flow.Launcher
|
|||
_animationSoundWMP?.Close();
|
||||
_animationSoundWPF?.Dispose();
|
||||
_viewModel.ActualApplicationThemeChanged -= ViewModel_ActualApplicationThemeChanged;
|
||||
UnregisterSoundEffectsEvent();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
|
|
|
|||
Loading…
Reference in a new issue