mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
[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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user