[Awake]QOL changes - New build ATRIOX_04132023 (#25486)

* Update control to make interaction responsive

* Rip out NLog in favor of standard logging

* Continuing to cleanup NLog stuff

* Simplifying the code more

* Instantly let go of power settings once cancellation requested.

* Cleanup and using built-in native constructs

* Update the API

* Moving towards using a queue instead of tasks

* Code cleanup

* Thread should be flagged as background

* Clean up constants, add docs

* Code cleanup

* Cleanup

* Cleanup

* Remove unnecessary using

* Fix package definition

* Fix NuGet packages

* Update expect.txt

* Remove NLog reference and add a build update in the planning doc

* Cleanup based on report

* More cleanup

* Adding back because the word is clearly somewhere, just not anywhere
I am able to find.

* Revert .net dependency upgrades
This commit is contained in:
Den
2023-05-14 11:42:38 -07:00
committed by GitHub
parent a3227da634
commit 0c5113e908
23 changed files with 740 additions and 671 deletions

View File

@@ -4,7 +4,6 @@
using System;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
@@ -17,18 +16,10 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Awake.Core;
using interop;
using Awake.Core.Models;
using Awake.Core.Native;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using NLog;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.System.Console;
using Windows.Win32.System.Power;
using Logger = NLog.Logger;
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8603 // Possible null reference return.
namespace Awake
{
@@ -40,7 +31,7 @@ namespace Awake
// Format of the build ID is: CODENAME_MMDDYYYY, where MMDDYYYY
// is representative of the date when the last change was made before
// the pull request is issued.
private static readonly string BuildId = "NOBLE_SIX_02162023";
private static readonly string BuildId = "ATRIOX_04132023";
private static Mutex? _mutex;
private static FileSystemWatcher? _watcher;
@@ -48,22 +39,21 @@ namespace Awake
private static bool _startedFromPowerToys;
public static Mutex LockMutex { get => _mutex; set => _mutex = value; }
private static Logger? _log;
public static Mutex? LockMutex { get => _mutex; set => _mutex = value; }
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private static PHANDLER_ROUTINE _handler;
private static SYSTEM_POWER_CAPABILITIES _powerCapabilities;
private static ConsoleEventHandler _handler;
private static SystemPowerCapabilities _powerCapabilities;
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private static ManualResetEvent _exitSignal = new ManualResetEvent(false);
private static int Main(string[] args)
{
// Log initialization needs to always happen before we test whether
// only one instance of Awake is running.
_log = LogManager.GetCurrentClassLogger();
_settingsUtils = new SettingsUtils();
LockMutex = new Mutex(true, Core.Constants.AppName, out bool instantiated);
Logger.InitializeLogger(Path.Combine("\\", Core.Constants.AppName, "Logs"));
if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredAwakeEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
{
@@ -71,20 +61,16 @@ namespace Awake
return 0;
}
LockMutex = new Mutex(true, InternalConstants.AppName, out bool instantiated);
if (!instantiated)
{
Exit(InternalConstants.AppName + " is already running! Exiting the application.", 1, _exitSignal, true);
Exit(Core.Constants.AppName + " is already running! Exiting the application.", 1, _exitSignal, true);
}
_settingsUtils = new SettingsUtils();
_log.Info($"Launching {InternalConstants.AppName}...");
_log.Info(FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion);
_log.Info($"Build: {BuildId}");
_log.Info($"OS: {Environment.OSVersion}");
_log.Info($"OS Build: {APIHelper.GetOperatingSystemBuild()}");
Logger.LogInfo($"Launching {Core.Constants.AppName}...");
Logger.LogInfo(FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion);
Logger.LogInfo($"Build: {BuildId}");
Logger.LogInfo($"OS: {Environment.OSVersion}");
Logger.LogInfo($"OS Build: {Manager.GetOperatingSystemBuild()}");
TaskScheduler.UnobservedTaskException += (sender, args) =>
{
@@ -94,21 +80,18 @@ namespace Awake
// To make it easier to diagnose future issues, let's get the
// system power capabilities and aggregate them in the log.
PInvoke.GetPwrCapabilities(out _powerCapabilities);
_log.Info(JsonSerializer.Serialize(_powerCapabilities));
Bridge.GetPwrCapabilities(out _powerCapabilities);
Logger.LogInfo(JsonSerializer.Serialize(_powerCapabilities));
_log.Info("Parsing parameters...");
Logger.LogInfo("Parsing parameters...");
Option<bool> configOption = new(
aliases: new[] { "--use-pt-config", "-c" },
getDefaultValue: () => false,
description: $"Specifies whether {InternalConstants.AppName} will be using the PowerToys configuration file for managing the state.")
description: $"Specifies whether {Core.Constants.AppName} will be using the PowerToys configuration file for managing the state.")
{
Argument = new Argument<bool>(() => false)
{
Arity = ArgumentArity.ZeroOrOne,
},
Required = false,
Arity = ArgumentArity.ZeroOrOne,
IsRequired = false,
};
Option<bool> displayOption = new(
@@ -116,11 +99,8 @@ namespace Awake
getDefaultValue: () => true,
description: "Determines whether the display should be kept awake.")
{
Argument = new Argument<bool>(() => false)
{
Arity = ArgumentArity.ZeroOrOne,
},
Required = false,
Arity = ArgumentArity.ZeroOrOne,
IsRequired = false,
};
Option<uint> timeOption = new(
@@ -128,35 +108,26 @@ namespace Awake
getDefaultValue: () => 0,
description: "Determines the interval, in seconds, during which the computer is kept awake.")
{
Argument = new Argument<uint>(() => 0)
{
Arity = ArgumentArity.ExactlyOne,
},
Required = false,
Arity = ArgumentArity.ExactlyOne,
IsRequired = false,
};
Option<int> pidOption = new(
aliases: new[] { "--pid", "-p" },
getDefaultValue: () => 0,
description: $"Bind the execution of {InternalConstants.AppName} to another process. When the process ends, the system will resume managing the current sleep/display mode.")
description: $"Bind the execution of {Core.Constants.AppName} to another process. When the process ends, the system will resume managing the current sleep and display state.")
{
Argument = new Argument<int>(() => 0)
{
Arity = ArgumentArity.ZeroOrOne,
},
Required = false,
Arity = ArgumentArity.ZeroOrOne,
IsRequired = false,
};
Option<string> expireAtOption = new(
aliases: new[] { "--expire-at", "-e" },
getDefaultValue: () => string.Empty,
description: $"Determines the end date/time when {InternalConstants.AppName} will back off and let the system manage the current sleep/display mode.")
description: $"Determines the end date/time when {Core.Constants.AppName} will back off and let the system manage the current sleep and display state.")
{
Argument = new Argument<string>(() => string.Empty)
{
Arity = ArgumentArity.ZeroOrOne,
},
Required = false,
Arity = ArgumentArity.ZeroOrOne,
IsRequired = false,
};
RootCommand? rootCommand = new()
@@ -168,49 +139,58 @@ namespace Awake
expireAtOption,
};
rootCommand.Description = InternalConstants.AppName;
rootCommand.Description = Core.Constants.AppName;
rootCommand.Handler = CommandHandler.Create<bool, bool, uint, int, string>(HandleCommandLineArguments);
_log.Info("Parameter setup complete. Proceeding to the rest of the app initiation...");
rootCommand.SetHandler(
HandleCommandLineArguments,
configOption,
displayOption,
timeOption,
pidOption,
expireAtOption);
return rootCommand.InvokeAsync(args).Result;
}
private static BOOL ExitHandler(uint ctrlType)
private static bool ExitHandler(ControlType ctrlType)
{
_log.Info($"Exited through handler with control type: {ctrlType}");
Logger.LogInfo($"Exited through handler with control type: {ctrlType}");
Exit("Exiting from the internal termination handler.", Environment.ExitCode, _exitSignal);
return false;
}
private static void Exit(string message, int exitCode, ManualResetEvent exitSignal, bool force = false)
{
_log.Info(message);
Logger.LogInfo(message);
APIHelper.CompleteExit(exitCode, exitSignal, force);
Manager.CompleteExit(exitCode, exitSignal, force);
}
private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, uint timeLimit, int pid, string expireAt)
{
_handler += ExitHandler;
APIHelper.SetConsoleControlHandler(_handler, true);
if (pid == 0)
{
_log.Info("No PID specified. Allocating console...");
APIHelper.AllocateConsole();
Logger.LogInfo("No PID specified. Allocating console...");
Manager.AllocateConsole();
_handler += new ConsoleEventHandler(ExitHandler);
Manager.SetConsoleControlHandler(_handler, true);
Trace.Listeners.Add(new ConsoleTraceListener());
}
else
{
_startedFromPowerToys = true;
}
_log.Info($"The value for --use-pt-config is: {usePtConfig}");
_log.Info($"The value for --display-on is: {displayOn}");
_log.Info($"The value for --time-limit is: {timeLimit}");
_log.Info($"The value for --pid is: {pid}");
_log.Info($"The value for --expire is: {expireAt}");
Logger.LogInfo($"The value for --use-pt-config is: {usePtConfig}");
Logger.LogInfo($"The value for --display-on is: {displayOn}");
Logger.LogInfo($"The value for --time-limit is: {timeLimit}");
Logger.LogInfo($"The value for --pid is: {pid}");
Logger.LogInfo($"The value for --expire-at is: {expireAt}");
// Start the monitor thread that will be used to track the current state.
Manager.StartMonitor();
if (usePtConfig)
{
@@ -218,7 +198,7 @@ namespace Awake
// and instead watch for changes in the file.
try
{
var eventHandle = new EventWaitHandle(false, EventResetMode.ManualReset, Constants.AwakeExitEvent());
var eventHandle = new EventWaitHandle(false, EventResetMode.ManualReset, interop.Constants.AwakeExitEvent());
new Thread(() =>
{
if (WaitHandle.WaitAny(new WaitHandle[] { _exitSignal, eventHandle }) == 1)
@@ -227,17 +207,17 @@ namespace Awake
}
}).Start();
TrayHelper.InitializeTray(InternalConstants.FullAppName, new Icon(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "images/awake.ico")), _exitSignal);
TrayHelper.InitializeTray(Core.Constants.FullAppName, new Icon(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "images/awake.ico")), _exitSignal);
string? settingsPath = _settingsUtils.GetSettingsFilePath(InternalConstants.AppName);
_log.Info($"Reading configuration file: {settingsPath}");
string? settingsPath = _settingsUtils!.GetSettingsFilePath(Core.Constants.AppName);
Logger.LogInfo($"Reading configuration file: {settingsPath}");
if (!File.Exists(settingsPath))
{
string? errorString = $"The settings file does not exist. Scaffolding default configuration...";
AwakeSettings scaffoldSettings = new AwakeSettings();
_settingsUtils.SaveSettings(JsonSerializer.Serialize(scaffoldSettings), InternalConstants.AppName);
_settingsUtils.SaveSettings(JsonSerializer.Serialize(scaffoldSettings), Core.Constants.AppName);
}
ScaffoldConfiguration(settingsPath);
@@ -245,8 +225,7 @@ namespace Awake
catch (Exception ex)
{
string? errorString = $"There was a problem with the configuration file. Make sure it exists.\n{ex.Message}";
_log.Info(errorString);
_log.Debug(errorString);
Logger.LogError(errorString);
}
}
else
@@ -264,16 +243,18 @@ namespace Awake
// converting the target date to seconds and then passing to SetupTimedKeepAwake
// because that way we're accounting for the user potentially changing their clock
// while Awake is running.
Logger.LogInfo($"Operating in thread ID {Environment.CurrentManagedThreadId}.");
SetupExpirableKeepAwake(expirationDateTime, displayOn);
}
else
{
_log.Info($"Target date is not in the future, therefore there is nothing to wait for.");
Logger.LogInfo($"Target date is not in the future, therefore there is nothing to wait for.");
}
}
catch
catch (Exception ex)
{
_log.Error($"Could not parse date string {expireAt} into a viable date.");
Logger.LogError($"Could not parse date string {expireAt} into a viable date.");
Logger.LogError(ex.Message);
}
}
else
@@ -295,7 +276,7 @@ namespace Awake
{
RunnerHelper.WaitForPowerToysRunner(pid, () =>
{
_log.Info($"Triggered PID-based exit handler for PID {pid}.");
Logger.LogInfo($"Triggered PID-based exit handler for PID {pid}.");
Exit("Terminating from process binding hook.", 0, _exitSignal, true);
});
}
@@ -330,7 +311,7 @@ namespace Awake
.Select(e => e.EventArgs)
.Subscribe(HandleAwakeConfigChange);
TrayHelper.SetTray(InternalConstants.FullAppName, new AwakeSettings(), _startedFromPowerToys);
TrayHelper.SetTray(Core.Constants.FullAppName, new AwakeSettings(), _startedFromPowerToys);
// Initially the file might not be updated, so we need to start processing
// settings right away.
@@ -338,19 +319,19 @@ namespace Awake
}
catch (Exception ex)
{
_log.Error($"An error occurred scaffolding the configuration. Error details: {ex.Message}");
Logger.LogError($"An error occurred scaffolding the configuration. Error details: {ex.Message}");
}
}
private static void SetupIndefiniteKeepAwake(bool displayOn)
{
APIHelper.SetIndefiniteKeepAwake(LogCompletedKeepAwakeThread, LogUnexpectedOrCancelledKeepAwakeThreadCompletion, displayOn);
Manager.SetIndefiniteKeepAwake(displayOn);
}
private static void HandleAwakeConfigChange(FileSystemEventArgs fileEvent)
{
_log.Info("Detected a settings file change. Updating configuration...");
_log.Info("Resetting keep-awake to normal state due to settings change.");
Logger.LogInfo("Detected a settings file change. Updating configuration...");
Logger.LogInfo("Resetting keep-awake to normal state due to settings change.");
ProcessSettings();
}
@@ -358,11 +339,11 @@ namespace Awake
{
try
{
AwakeSettings settings = _settingsUtils.GetSettings<AwakeSettings>(InternalConstants.AppName);
AwakeSettings settings = _settingsUtils!.GetSettings<AwakeSettings>(Core.Constants.AppName);
if (settings != null)
{
_log.Info($"Identified custom time shortcuts for the tray: {settings.Properties.CustomTrayTimes.Count}");
Logger.LogInfo($"Identified custom time shortcuts for the tray: {settings.Properties.CustomTrayTimes.Count}");
switch (settings.Properties.Mode)
{
@@ -396,60 +377,45 @@ namespace Awake
default:
{
string? errorMessage = "Unknown mode of operation. Check config file.";
_log.Info(errorMessage);
_log.Debug(errorMessage);
Logger.LogError(errorMessage);
break;
}
}
TrayHelper.SetTray(InternalConstants.FullAppName, settings, _startedFromPowerToys);
TrayHelper.SetTray(Core.Constants.FullAppName, settings, _startedFromPowerToys);
}
else
{
string? errorMessage = "Settings are null.";
_log.Info(errorMessage);
_log.Debug(errorMessage);
Logger.LogError(errorMessage);
}
}
catch (Exception ex)
{
string? errorMessage = $"There was a problem reading the configuration file. Error: {ex.GetType()} {ex.Message}";
_log.Info(errorMessage);
_log.Debug(errorMessage);
Logger.LogError(errorMessage);
}
}
private static void SetupNoKeepAwake()
{
_log.Info($"Operating in passive mode (computer's standard power plan). No custom keep awake settings enabled.");
Logger.LogInfo($"Operating in passive mode (computer's standard power plan). No custom keep awake settings enabled.");
APIHelper.SetNoKeepAwake();
Manager.SetNoKeepAwake();
}
private static void SetupExpirableKeepAwake(DateTimeOffset expireAt, bool displayOn)
{
_log.Info($"Expirable keep-awake. Expected expiration date/time: {expireAt} with display on setting set to {displayOn}.");
Logger.LogInfo($"Expirable keep-awake. Expected expiration date/time: {expireAt} with display on setting set to {displayOn}.");
APIHelper.SetExpirableKeepAwake(expireAt, LogCompletedKeepAwakeThread, LogUnexpectedOrCancelledKeepAwakeThreadCompletion, displayOn);
Manager.SetExpirableKeepAwake(expireAt, displayOn);
}
private static void SetupTimedKeepAwake(uint time, bool displayOn)
{
_log.Info($"Timed keep-awake. Expected runtime: {time} seconds with display on setting set to {displayOn}.");
Logger.LogInfo($"Timed keep-awake. Expected runtime: {time} seconds with display on setting set to {displayOn}.");
APIHelper.SetTimedKeepAwake(time, LogCompletedKeepAwakeThread, LogUnexpectedOrCancelledKeepAwakeThreadCompletion, displayOn);
}
private static void LogUnexpectedOrCancelledKeepAwakeThreadCompletion()
{
string? errorMessage = "The keep awake thread was terminated early.";
_log.Info(errorMessage);
_log.Debug(errorMessage);
}
private static void LogCompletedKeepAwakeThread()
{
_log.Info($"Exited keep awake thread successfully.");
Manager.SetTimedKeepAwake(time, displayOn);
}
}
}