Disable sleep idle timeout when audio is playing via SetThreadExecutionState.

This commit is contained in:
2026-08-28 23:36:47 -04:00
parent 32547c4f4c
commit ccea149df0
2 changed files with 63 additions and 1 deletions

View File

@@ -102,5 +102,6 @@
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun> <PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
<PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed> <PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed>
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed> <PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -1,6 +1,9 @@
using Harmonia.Core.Player;
using Harmonia.WinUI.Session; using Harmonia.WinUI.Session;
using Microsoft.UI.Windowing; using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using System;
using System.Runtime.InteropServices;
namespace Harmonia.WinUI; namespace Harmonia.WinUI;
@@ -8,16 +11,23 @@ public sealed partial class MainWindow : Window
{ {
private const string ApplicationTitle = "Harmonia"; private const string ApplicationTitle = "Harmonia";
[LibraryImport("kernel32.dll", SetLastError = true)]
private static partial EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
private readonly ISessionService _sessionService; private readonly ISessionService _sessionService;
private readonly ISessionTracker _sessionTracker; private readonly ISessionTracker _sessionTracker;
private readonly IAudioPlayer _audioPlayer;
private bool _isSaved; private bool _isSaved;
public MainWindow(ISessionService sessionService, ISessionTracker sessionTracker) public MainWindow(ISessionService sessionService, ISessionTracker sessionTracker, IAudioPlayer audioPlayer)
{ {
_sessionService = sessionService; _sessionService = sessionService;
_sessionTracker = sessionTracker; _sessionTracker = sessionTracker;
_audioPlayer = audioPlayer;
_audioPlayer.PropertyChanged += OnAudioPlayerPropertyChanged;
InitializeComponent(); InitializeComponent();
InitializeTitleBar(); InitializeTitleBar();
InitializeWindowState(); InitializeWindowState();
@@ -54,6 +64,8 @@ public sealed partial class MainWindow : Window
private async void OnMainWindowClosed(object sender, WindowEventArgs args) private async void OnMainWindowClosed(object sender, WindowEventArgs args)
{ {
EnableSleepIdleTimeout();
if (_isSaved) if (_isSaved)
return; return;
@@ -65,4 +77,53 @@ public sealed partial class MainWindow : Window
_isSaved = true; _isSaved = true;
Close(); Close();
} }
private void OnAudioPlayerPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(_audioPlayer.State):
OnAudioPlayerStateChanged(sender, e);
break;
}
}
private void OnAudioPlayerStateChanged(object? sender, EventArgs e)
{
// SetThreadExecutionState is per-thread, so always marshal to the UI thread to
// guarantee the ES_CONTINUOUS flags are set and cleared on the same thread.
DispatcherQueue.TryEnqueue(() =>
{
switch (_audioPlayer.State)
{
case Core.Engine.AudioPlaybackState.Playing:
DisableSleepIdleTimeout();
break;
default:
EnableSleepIdleTimeout();
break;
}
});
}
private static void EnableSleepIdleTimeout()
{
// Clear EXECUTION_STATE flags to allow the system to idle to sleep normally.
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);
}
private static void DisableSleepIdleTimeout()
{
// Prevent the system sleep idle time-out while still allowing the display to sleep.
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS | EXECUTION_STATE.ES_SYSTEM_REQUIRED);
}
[Flags]
private enum EXECUTION_STATE : uint
{
ES_CONTINUOUS = 0x80000000,
ES_SYSTEM_REQUIRED = 0x00000001,
ES_DISPLAY_REQUIRED = 0x00000002,
ES_AWAYMODE_REQUIRED = 0x00000040
}
} }