Skip to content
Merged
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
7 changes: 6 additions & 1 deletion Flow.Launcher.Infrastructure/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,10 @@
EVENT_OBJECT_HIDE
EVENT_SYSTEM_DIALOGEND

DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS
WM_POWERBROADCAST
PBT_APMRESUMEAUTOMATIC
PBT_APMRESUMEAUTOMATIC
PBT_APMRESUMESUSPEND
PowerRegisterSuspendResumeNotification
PowerUnregisterSuspendResumeNotification
DeviceNotifyCallbackRoutine

Check warning on line 94 in Flow.Launcher.Infrastructure/NativeMethods.txt

View workflow job for this annotation

GitHub Actions / build

Method, type or constant "DeviceNotifyCallbackRoutine" not found
104 changes: 101 additions & 3 deletions Flow.Launcher.Infrastructure/Win32Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
using Microsoft.Win32.SafeHandles;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Dwm;

Check warning on line 21 in Flow.Launcher.Infrastructure/Win32Helper.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`Dwm` is not a recognized word. (unrecognized-spelling)
using Windows.Win32.System.Power;
using Windows.Win32.System.Threading;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using Windows.Win32.UI.Shell.Common;
Expand All @@ -43,7 +44,7 @@
{
var cloaked = cloak ? 1 : 0;

return PInvoke.DwmSetWindowAttribute(

Check warning on line 47 in Flow.Launcher.Infrastructure/Win32Helper.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`Dwm` is not a recognized word. (unrecognized-spelling)
GetWindowHandle(window),
DWMWINDOWATTRIBUTE.DWMWA_CLOAK,
&cloaked,
Expand All @@ -54,13 +55,13 @@
{
var backdropType = backdrop switch
{
BackdropTypes.Acrylic => DWM_SYSTEMBACKDROP_TYPE.DWMSBT_TRANSIENTWINDOW,

Check warning on line 58 in Flow.Launcher.Infrastructure/Win32Helper.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`SYSTEMBACKDROP` is not a recognized word. (unrecognized-spelling)
BackdropTypes.Mica => DWM_SYSTEMBACKDROP_TYPE.DWMSBT_MAINWINDOW,

Check warning on line 59 in Flow.Launcher.Infrastructure/Win32Helper.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`SYSTEMBACKDROP` is not a recognized word. (unrecognized-spelling)

Check warning on line 59 in Flow.Launcher.Infrastructure/Win32Helper.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`DWMSBT` is not a recognized word. (unrecognized-spelling)
BackdropTypes.MicaAlt => DWM_SYSTEMBACKDROP_TYPE.DWMSBT_TABBEDWINDOW,

Check warning on line 60 in Flow.Launcher.Infrastructure/Win32Helper.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`SYSTEMBACKDROP` is not a recognized word. (unrecognized-spelling)

Check warning on line 60 in Flow.Launcher.Infrastructure/Win32Helper.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`DWMSBT` is not a recognized word. (unrecognized-spelling)
_ => DWM_SYSTEMBACKDROP_TYPE.DWMSBT_AUTO

Check warning on line 61 in Flow.Launcher.Infrastructure/Win32Helper.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`SYSTEMBACKDROP` is not a recognized word. (unrecognized-spelling)

Check warning on line 61 in Flow.Launcher.Infrastructure/Win32Helper.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`DWMSBT` is not a recognized word. (unrecognized-spelling)
};

return PInvoke.DwmSetWindowAttribute(

Check warning on line 64 in Flow.Launcher.Infrastructure/Win32Helper.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`Dwm` is not a recognized word. (unrecognized-spelling)
GetWindowHandle(window),
DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE,
&backdropType,
Expand Down Expand Up @@ -338,9 +339,6 @@
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
Expand Down Expand Up @@ -918,5 +916,105 @@
}

#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
}
}
99 changes: 70 additions & 29 deletions Flow.Launcher/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -61,8 +62,9 @@
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;
Expand Down Expand Up @@ -93,6 +95,7 @@
UpdatePosition();

InitSoundEffects();
RegisterSoundEffectsEvent();
DataObject.AddPastingHandler(QueryTextBox, QueryTextBox_OnPaste);
_viewModel.ActualApplicationThemeChanged += ViewModel_ActualApplicationThemeChanged;
}
Expand Down Expand Up @@ -666,16 +669,6 @@
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;
Expand All @@ -687,31 +680,78 @@

private void InitSoundEffects()
{
if (_settings.WMPInstalled)
lock (_soundLock)
{
animationSoundWMP?.Close();
animationSoundWMP = new MediaPlayer();
animationSoundWMP.Open(new Uri(AppContext.BaseDirectory + "Resources\\open.wav"));
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();
}
}
else
}

private void SoundPlay()
{
lock (_soundLock)
{
animationSoundWPF?.Dispose();
animationSoundWPF = new SoundPlayer(AppContext.BaseDirectory + "Resources\\open.wav");
animationSoundWPF.Load();
if (_settings.WMPInstalled)
{
_animationSoundWMP.Position = TimeSpan.Zero;
_animationSoundWMP.Volume = _settings.SoundVolume / 100.0;
_animationSoundWMP.Play();
}
else
{
_animationSoundWPF.Play();
}
}
}

private void SoundPlay()
private void RegisterSoundEffectsEvent()
{
if (_settings.WMPInstalled)
// 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
{
animationSoundWMP.Position = TimeSpan.Zero;
animationSoundWMP.Volume = _settings.SoundVolume / 100.0;
animationSoundWMP.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();
});
}
else
catch (Exception e)
{
App.API.LogException(ClassName, "Failed to register sound effect event", e);
}
}

private static void UnregisterSoundEffectsEvent()
{
try
{
Win32Helper.UnregisterSleepModeListener();
}
catch (Exception e)
{
animationSoundWPF.Play();
App.API.LogException(ClassName, "Failed to unregister sound effect event", e);
}
}

Expand Down Expand Up @@ -818,7 +858,7 @@

public void UpdatePosition()
{
// Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910

Check failure on line 861 in Flow.Launcher/MainWindow.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`work around` matches a line_forbidden.patterns entry: `\bwork[- ]arounds?\b`. (forbidden-pattern)
if (_viewModel.IsDialogJumpWindowUnderDialog())
{
InitializeDialogJumpPosition();
Expand All @@ -842,7 +882,7 @@

private void InitializePosition()
{
// Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910

Check failure on line 885 in Flow.Launcher/MainWindow.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`work around` matches a line_forbidden.patterns entry: `\bwork[- ]arounds?\b`. (forbidden-pattern)
InitializePositionInner();
InitializePositionInner();
return;
Expand Down Expand Up @@ -1436,9 +1476,10 @@
{
_hwndSource?.Dispose();
_notifyIcon?.Dispose();
animationSoundWMP?.Close();
animationSoundWPF?.Dispose();
_animationSoundWMP?.Close();
_animationSoundWPF?.Dispose();
_viewModel.ActualApplicationThemeChanged -= ViewModel_ActualApplicationThemeChanged;
UnregisterSoundEffectsEvent();
}

_disposed = true;
Expand Down
Loading