mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge pull request #4024 from Flow-Launcher/power_enhancement
Use sleep mode listener to fix modern standby sleep mode issue
This commit is contained in:
commit
1b7a475631
3 changed files with 178 additions and 34 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?.Invoke();
|
||||
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?.Invoke();
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Media;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
|
@ -61,8 +62,9 @@ namespace Flow.Launcher
|
|||
private bool _isArrowKeyPressed = false;
|
||||
|
||||
// Window Sound Effects
|
||||
private MediaPlayer animationSoundWMP;
|
||||
private SoundPlayer animationSoundWPF;
|
||||
private MediaPlayer _animationSoundWMP;
|
||||
private SoundPlayer _animationSoundWPF;
|
||||
private readonly Lock _soundLock = new();
|
||||
|
||||
// Window WndProc
|
||||
private HwndSource _hwndSource;
|
||||
|
|
@ -93,6 +95,7 @@ namespace Flow.Launcher
|
|||
UpdatePosition();
|
||||
|
||||
InitSoundEffects();
|
||||
RegisterSoundEffectsEvent();
|
||||
DataObject.AddPastingHandler(QueryTextBox, QueryTextBox_OnPaste);
|
||||
_viewModel.ActualApplicationThemeChanged += ViewModel_ActualApplicationThemeChanged;
|
||||
}
|
||||
|
|
@ -666,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;
|
||||
|
|
@ -687,31 +680,78 @@ namespace Flow.Launcher
|
|||
|
||||
private void InitSoundEffects()
|
||||
{
|
||||
if (_settings.WMPInstalled)
|
||||
lock (_soundLock)
|
||||
{
|
||||
animationSoundWMP?.Close();
|
||||
animationSoundWMP = new MediaPlayer();
|
||||
animationSoundWMP.Open(new Uri(AppContext.BaseDirectory + "Resources\\open.wav"));
|
||||
}
|
||||
else
|
||||
{
|
||||
animationSoundWPF?.Dispose();
|
||||
animationSoundWPF = new SoundPlayer(AppContext.BaseDirectory + "Resources\\open.wav");
|
||||
animationSoundWPF.Load();
|
||||
if (_settings.WMPInstalled)
|
||||
{
|
||||
_animationSoundWMP?.Close();
|
||||
_animationSoundWMP = new MediaPlayer();
|
||||
_animationSoundWMP.Open(new Uri(AppContext.BaseDirectory + "Resources\\open.wav"));
|
||||
}
|
||||
else
|
||||
{
|
||||
_animationSoundWPF?.Dispose();
|
||||
_animationSoundWPF = new SoundPlayer(AppContext.BaseDirectory + "Resources\\open.wav");
|
||||
_animationSoundWPF.Load();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SoundPlay()
|
||||
{
|
||||
if (_settings.WMPInstalled)
|
||||
lock (_soundLock)
|
||||
{
|
||||
animationSoundWMP.Position = TimeSpan.Zero;
|
||||
animationSoundWMP.Volume = _settings.SoundVolume / 100.0;
|
||||
animationSoundWMP.Play();
|
||||
if (_settings.WMPInstalled)
|
||||
{
|
||||
_animationSoundWMP.Position = TimeSpan.Zero;
|
||||
_animationSoundWMP.Volume = _settings.SoundVolume / 100.0;
|
||||
_animationSoundWMP.Play();
|
||||
}
|
||||
else
|
||||
{
|
||||
_animationSoundWPF.Play();
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
animationSoundWPF.Play();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1436,9 +1476,10 @@ namespace Flow.Launcher
|
|||
{
|
||||
_hwndSource?.Dispose();
|
||||
_notifyIcon?.Dispose();
|
||||
animationSoundWMP?.Close();
|
||||
animationSoundWPF?.Dispose();
|
||||
_animationSoundWMP?.Close();
|
||||
_animationSoundWPF?.Dispose();
|
||||
_viewModel.ActualApplicationThemeChanged -= ViewModel_ActualApplicationThemeChanged;
|
||||
UnregisterSoundEffectsEvent();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
|
|
|
|||
Loading…
Reference in a new issue