Awake Updates - TILLSON_11272024 (#36049)

* Update with bug fixes for tray icon and support for parent process

* Process information enum

* Update the docs

* Fix spelling

* Make sure that PID is used in PT config flow

* Logic for checks based on #34148

* Update with link to PR

* Fixes #34717

* Small cleanup

* Proper task segmentation in a function

* Cleanup the code

* Fix synchronization context issue

* Update planning doc

* Test disabling caching to see if that manages to pass CI

* Cleanup to make sure that we're logging things properly.

* Update ci.yml

* Disable cache to pass CI

* Retry logic

* Cleanup

* Code cleanup

* Fixes #35848

* Update notes and codename

* After third attempt, log error instead of throwing exception

* More cleanup to avoid double execution

* Add expected word

* Safeguards for bad values for timed keep-awake

* More updates to make sure I am using uint

* Update error message

* Update packages

* Fix notice and revert CsWinRT upgrade

* Codename update

* Update expect.txt

* Update the struct

* Ensuring we're properly awaiting tray initialization

* Update to make sure tray reflects the bound process

* Cleanup, proper JSON serialization for logs.

* Not needed.

* Add command validation logic

* Moving the initialization logic earlier

* Make sure we show the display state in the tooltip

* Update tray string

* Update src/modules/awake/Awake/Core/Manager.cs

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>

* Update src/modules/awake/Awake/Core/Manager.cs

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>

* Update src/modules/awake/Awake/Core/Manager.cs

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>

* Update src/modules/awake/Awake/Core/Manager.cs

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>

* Update logic for icon resets

* Update doc

* Simplify function for setting mode shell icon

* Issues should be properly linked

* Minor cleanup

* Update timed behavior

---------

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Clint Rutkas <clint@rutkas.com>
This commit is contained in:
Den Delimarsky 🔐
2024-12-08 22:41:05 -08:00
committed by GitHub
parent 3aec0a06ac
commit ef672b5564
16 changed files with 506 additions and 224 deletions

View File

@@ -4,8 +4,8 @@
using System;
using System.CommandLine;
using System.CommandLine.Parsing;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -15,7 +15,6 @@ using System.Reflection;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Awake.Core;
using Awake.Core.Models;
using Awake.Core.Native;
@@ -28,33 +27,34 @@ namespace Awake
{
internal sealed class Program
{
private static Mutex? _mutex;
private static readonly string[] _aliasesConfigOption = ["--use-pt-config", "-c"];
private static readonly string[] _aliasesDisplayOption = ["--display-on", "-d"];
private static readonly string[] _aliasesTimeOption = ["--time-limit", "-t"];
private static readonly string[] _aliasesPidOption = ["--pid", "-p"];
private static readonly string[] _aliasesExpireAtOption = ["--expire-at", "-e"];
private static readonly string[] _aliasesParentPidOption = ["--use-parent-pid", "-u"];
private static readonly JsonSerializerOptions _serializerOptions = new() { IncludeFields = true };
private static readonly ETWTrace _etwTrace = new();
private static FileSystemWatcher? _watcher;
private static SettingsUtils? _settingsUtils;
private static ETWTrace _etwTrace = new ETWTrace();
private static bool _startedFromPowerToys;
public static Mutex? LockMutex { get => _mutex; set => _mutex = value; }
public static Mutex? LockMutex { get; set; }
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
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.
internal static readonly string[] AliasesConfigOption = ["--use-pt-config", "-c"];
internal static readonly string[] AliasesDisplayOption = ["--display-on", "-d"];
internal static readonly string[] AliasesTimeOption = ["--time-limit", "-t"];
internal static readonly string[] AliasesPidOption = ["--pid", "-p"];
internal static readonly string[] AliasesExpireAtOption = ["--expire-at", "-e"];
internal static readonly string[] AliasesParentPidOption = ["--use-parent-pid", "-u"];
private static readonly Icon _defaultAwakeIcon = new(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets/Awake/awake.ico"));
private static int Main(string[] args)
private static async Task<int> Main(string[] args)
{
_settingsUtils = new SettingsUtils();
LockMutex = new Mutex(true, Core.Constants.AppName, out bool instantiated);
Logger.InitializeLogger(Path.Combine("\\", Core.Constants.AppName, "Logs"));
try
@@ -62,7 +62,7 @@ namespace Awake
string appLanguage = LanguageHelper.LoadLanguage();
if (!string.IsNullOrEmpty(appLanguage))
{
System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo(appLanguage);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(appLanguage);
}
}
catch (CultureNotFoundException ex)
@@ -70,6 +70,8 @@ namespace Awake
Logger.LogError("CultureNotFoundException: " + ex.Message);
}
await TrayHelper.InitializeTray(TrayHelper.DefaultAwakeIcon, Core.Constants.FullAppName);
AppDomain.CurrentDomain.ProcessExit += (_, _) => TrayHelper.RunOnMainThread(() => LockMutex?.ReleaseMutex());
AppDomain.CurrentDomain.UnhandledException += AwakeUnhandledExceptionCatcher;
if (!instantiated)
@@ -103,46 +105,76 @@ namespace Awake
// To make it easier to diagnose future issues, let's get the
// system power capabilities and aggregate them in the log.
Bridge.GetPwrCapabilities(out _powerCapabilities);
Logger.LogInfo(JsonSerializer.Serialize(_powerCapabilities));
Logger.LogInfo(JsonSerializer.Serialize(_powerCapabilities, _serializerOptions));
Logger.LogInfo("Parsing parameters...");
var configOption = new Option<bool>(AliasesConfigOption, () => false, Resources.AWAKE_CMD_HELP_CONFIG_OPTION)
Option<bool> configOption = new(_aliasesConfigOption, () => false, Resources.AWAKE_CMD_HELP_CONFIG_OPTION)
{
Arity = ArgumentArity.ZeroOrOne,
IsRequired = false,
};
var displayOption = new Option<bool>(AliasesDisplayOption, () => true, Resources.AWAKE_CMD_HELP_DISPLAY_OPTION)
Option<bool> displayOption = new(_aliasesDisplayOption, () => true, Resources.AWAKE_CMD_HELP_DISPLAY_OPTION)
{
Arity = ArgumentArity.ZeroOrOne,
IsRequired = false,
};
var timeOption = new Option<uint>(AliasesTimeOption, () => 0, Resources.AWAKE_CMD_HELP_TIME_OPTION)
Option<uint> timeOption = new(_aliasesTimeOption, () => 0, Resources.AWAKE_CMD_HELP_TIME_OPTION)
{
Arity = ArgumentArity.ExactlyOne,
IsRequired = false,
};
var pidOption = new Option<int>(AliasesPidOption, () => 0, Resources.AWAKE_CMD_HELP_PID_OPTION)
Option<int> pidOption = new(_aliasesPidOption, () => 0, Resources.AWAKE_CMD_HELP_PID_OPTION)
{
Arity = ArgumentArity.ZeroOrOne,
IsRequired = false,
};
var expireAtOption = new Option<string>(AliasesExpireAtOption, () => string.Empty, Resources.AWAKE_CMD_HELP_EXPIRE_AT_OPTION)
Option<string> expireAtOption = new(_aliasesExpireAtOption, () => string.Empty, Resources.AWAKE_CMD_HELP_EXPIRE_AT_OPTION)
{
Arity = ArgumentArity.ZeroOrOne,
IsRequired = false,
};
var parentPidOption = new Option<bool>(AliasesParentPidOption, () => false, Resources.AWAKE_CMD_PARENT_PID_OPTION)
Option<bool> parentPidOption = new(_aliasesParentPidOption, () => false, Resources.AWAKE_CMD_PARENT_PID_OPTION)
{
Arity = ArgumentArity.ZeroOrOne,
IsRequired = false,
};
timeOption.AddValidator(result =>
{
if (result.Tokens.Count != 0 && !uint.TryParse(result.Tokens[0].Value, out _))
{
string errorMessage = $"Interval in --time-limit could not be parsed correctly. Check that the value is valid and doesn't exceed 4,294,967,295. Value used: {result.Tokens[0].Value}.";
Logger.LogError(errorMessage);
result.ErrorMessage = errorMessage;
}
});
pidOption.AddValidator(result =>
{
if (result.Tokens.Count != 0 && !int.TryParse(result.Tokens[0].Value, out _))
{
string errorMessage = $"PID value in --pid could not be parsed correctly. Check that the value is valid and falls within the boundaries of Windows PID process limits. Value used: {result.Tokens[0].Value}.";
Logger.LogError(errorMessage);
result.ErrorMessage = errorMessage;
}
});
expireAtOption.AddValidator(result =>
{
if (result.Tokens.Count != 0 && !DateTimeOffset.TryParse(result.Tokens[0].Value, out _))
{
string errorMessage = $"Date and time value in --expire-at could not be parsed correctly. Check that the value is valid date and time. Refer to https://aka.ms/powertoys/awake for format examples. Value used: {result.Tokens[0].Value}.";
Logger.LogError(errorMessage);
result.ErrorMessage = errorMessage;
}
});
RootCommand? rootCommand =
[
configOption,
@@ -207,9 +239,7 @@ namespace Awake
// Start the monitor thread that will be used to track the current state.
Manager.StartMonitor();
TrayHelper.InitializeTray(_defaultAwakeIcon, Core.Constants.FullAppName);
var eventHandle = new EventWaitHandle(false, EventResetMode.ManualReset, PowerToys.Interop.Constants.AwakeExitEvent());
EventWaitHandle eventHandle = new(false, EventResetMode.ManualReset, PowerToys.Interop.Constants.AwakeExitEvent());
new Thread(() =>
{
WaitHandle.WaitAny([eventHandle]);
@@ -219,7 +249,8 @@ namespace Awake
if (usePtConfig)
{
// Configuration file is used, therefore we disregard any other command-line parameter
// and instead watch for changes in the file.
// and instead watch for changes in the file. This is used as a priority against all other arguments,
// so if --use-pt-config is applied the rest of the arguments are irrelevant.
Manager.IsUsingPowerToysConfig = true;
try
@@ -259,13 +290,14 @@ namespace Awake
// Second, we snap to process-based execution. Because this is something that
// is snapped to a running entity, we only want to enable the ability to set
// indefinite keep-awake with the display settings that the user wants to set.
// In this context, manual (explicit) PID takes precedence over parent PID.
int targetPid = pid != 0 ? pid : useParentPid ? Manager.GetParentProcess()?.Id ?? 0 : 0;
if (targetPid != 0)
{
Logger.LogInfo($"Bound to target process: {targetPid}");
Manager.SetIndefiniteKeepAwake(displayOn);
Manager.SetIndefiniteKeepAwake(displayOn, targetPid);
RunnerHelper.WaitForPowerToysRunner(targetPid, () =>
{
@@ -338,8 +370,8 @@ namespace Awake
private static void SetupFileSystemWatcher(string settingsPath)
{
var directory = Path.GetDirectoryName(settingsPath)!;
var fileName = Path.GetFileName(settingsPath);
string directory = Path.GetDirectoryName(settingsPath)!;
string fileName = Path.GetFileName(settingsPath);
_watcher = new FileSystemWatcher
{
@@ -364,7 +396,7 @@ namespace Awake
private static void InitializeSettings()
{
var settings = Manager.ModuleSettings?.GetSettings<AwakeSettings>(Core.Constants.AppName) ?? new AwakeSettings();
AwakeSettings settings = Manager.ModuleSettings?.GetSettings<AwakeSettings>(Core.Constants.AppName) ?? new AwakeSettings();
TrayHelper.SetTray(settings, _startedFromPowerToys);
}
@@ -385,7 +417,7 @@ namespace Awake
{
try
{
var settings = _settingsUtils!.GetSettings<AwakeSettings>(Core.Constants.AppName)
AwakeSettings settings = _settingsUtils!.GetSettings<AwakeSettings>(Core.Constants.AppName)
?? throw new InvalidOperationException("Settings are null.");
Logger.LogInfo($"Identified custom time shortcuts for the tray: {settings.Properties.CustomTrayTimes.Count}");