diff --git a/src/modules/espresso/Espresso.Shell/Core/APIHelper.cs b/src/modules/espresso/Espresso.Shell/Core/APIHelper.cs index 3d5b29b4a8..f7383b3b3c 100644 --- a/src/modules/espresso/Espresso.Shell/Core/APIHelper.cs +++ b/src/modules/espresso/Espresso.Shell/Core/APIHelper.cs @@ -4,6 +4,8 @@ using System; using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; namespace Espresso.Shell.Core { @@ -22,6 +24,9 @@ namespace Espresso.Shell.Core /// public class APIHelper { + private static CancellationTokenSource TokenSource = new CancellationTokenSource(); + private static CancellationToken ThreadToken; + // More details about the API used: https://docs.microsoft.com/windows/win32/api/winbase/nf-winbase-setthreadexecutionstate [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags); @@ -58,6 +63,7 @@ namespace Espresso.Shell.Core public static bool SetNormalKeepAwake() { + //TokenSource.Cancel(); return SetAwakeState(EXECUTION_STATE.ES_CONTINUOUS); } @@ -73,43 +79,74 @@ namespace Espresso.Shell.Core } } - public static bool SetTimedKeepAwake(long seconds, bool keepDisplayOn = true) + public static void SetTimedKeepAwake(long seconds, Action callback, bool keepDisplayOn = true) { + ThreadToken = TokenSource.Token; + + try + { + Task.Run(() => RunTimedLoop(seconds, keepDisplayOn), ThreadToken).ContinueWith((result) => callback(result.Result)); + } + catch (OperationCanceledException e) + { + Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}"); + } + finally + { + TokenSource.Dispose(); + } + + } + + private static bool RunTimedLoop(long seconds, bool keepDisplayOn = true) + { + bool success; + + // In case cancellation was already requested. + //ThreadToken.ThrowIfCancellationRequested(); + if (keepDisplayOn) { - var success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); + success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); if (success) { - RunTimedLoop(seconds); - return true; + Console.WriteLine("Timed keep-awake with display on."); + var startTime = DateTime.UtcNow; + while (DateTime.UtcNow - startTime < TimeSpan.FromSeconds(seconds)) + { + if (ThreadToken.IsCancellationRequested) + { + ThreadToken.ThrowIfCancellationRequested(); + } + } + return success; } else { - return false; + return success; } } else { - var success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); + success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); if (success) { - RunTimedLoop(seconds); - return true; + Console.WriteLine("Timed keep-awake with display off."); + var startTime = DateTime.UtcNow; + while (DateTime.UtcNow - startTime < TimeSpan.FromSeconds(seconds)) + { + if (ThreadToken.IsCancellationRequested) + { + ThreadToken.ThrowIfCancellationRequested(); + } + } + return success; } else { - return false; + return success; } } } - - private static void RunTimedLoop(long seconds) - { - var startTime = DateTime.UtcNow; - while (DateTime.UtcNow - startTime < TimeSpan.FromSeconds(seconds)) - { - // We do nothing. - } - } } } diff --git a/src/modules/espresso/Espresso.Shell/Program.cs b/src/modules/espresso/Espresso.Shell/Program.cs index a6807f5c3f..a44d21c98a 100644 --- a/src/modules/espresso/Espresso.Shell/Program.cs +++ b/src/modules/espresso/Espresso.Shell/Program.cs @@ -141,20 +141,20 @@ namespace Espresso.Shell else { // Timed keep-awake. - bool success = APIHelper.SetTimedKeepAwake(timeLimit, displayOn); - if (success) - { - Console.WriteLine($"Finished execution of timed keep-awake."); + APIHelper.SetTimedKeepAwake(timeLimit, LogTimedKeepAwakeCompletion, displayOn); + //if (success) + //{ + // Console.WriteLine($"Finished execution of timed keep-awake."); - // Because the timed keep-awake execution completed, there is no reason for - // Espresso to stay alive - I will just shut down the application until it's - // launched again by the user. - Environment.Exit(0); - } - else - { - Console.WriteLine("Could not set up the state to be timed keep awake."); - } + // // Because the timed keep-awake execution completed, there is no reason for + // // Espresso to stay alive - I will just shut down the application until it's + // // launched again by the user. + // Environment.Exit(0); + //} + //else + //{ + // Console.WriteLine("Could not set up the state to be timed keep awake."); + //} } } @@ -163,6 +163,8 @@ namespace Espresso.Shell private static void HandleEspressoConfigChange(object sender, FileSystemEventArgs e) { + Console.WriteLine("Resetting keep-awake to normal state due to settings change."); + ResetNormalPowerState(); Console.WriteLine("Detected a file change. Reacting..."); ProcessSettings(e.FullPath); } @@ -210,22 +212,22 @@ namespace Espresso.Shell long computedTime = (settings.Properties.Hours.Value * 60 * 60) + (settings.Properties.Minutes.Value * 60); Console.WriteLine($"In timed keep-awake mode. Expecting to be awake for {computedTime} seconds."); - bool success = APIHelper.SetTimedKeepAwake(computedTime, settings.Properties.KeepDisplayOn.Value); - if (success) - { - Console.WriteLine($"Finished execution of timed keep-awake."); + APIHelper.SetTimedKeepAwake(computedTime, LogTimedKeepAwakeCompletion, settings.Properties.KeepDisplayOn.Value); + //if (success) + //{ + // Console.WriteLine($"Finished execution of timed keep-awake."); - ResetNormalPowerState(); - } - else - { - Console.WriteLine("Could not set up the state to be timed keep awake."); - } + // ResetNormalPowerState(); + //} + //else + //{ + // Console.WriteLine("Could not set up the state to be timed keep awake."); + //} break; } default: { - ForceExit("Could not select the right mode of operation. Existing...", 1); + Console.WriteLine("Unknown mode of operation. Check config file."); break; } } @@ -246,6 +248,11 @@ namespace Espresso.Shell } } + private static void LogTimedKeepAwakeCompletion(bool result) + { + Console.Write($"Completed timed keep-awake successfully: {result}"); + } + private static void ResetNormalPowerState() { bool success = APIHelper.SetNormalKeepAwake();