mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-11-27 14:59:16 +08: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:
parent
a3227da634
commit
0c5113e908
19
.github/actions/spell-check/expect.txt
vendored
19
.github/actions/spell-check/expect.txt
vendored
@ -80,6 +80,7 @@ APeriod
|
||||
apidl
|
||||
APIENTRY
|
||||
APIIs
|
||||
Apm
|
||||
APPBARDATA
|
||||
appdata
|
||||
APPEXECLINK
|
||||
@ -89,7 +90,6 @@ Applets
|
||||
Applicationcan
|
||||
applicationconfiguration
|
||||
applicationframehost
|
||||
applog
|
||||
appmanifest
|
||||
APPNAME
|
||||
appref
|
||||
@ -124,6 +124,7 @@ atlcom
|
||||
atleast
|
||||
atlfile
|
||||
atlstr
|
||||
ATRIOX
|
||||
Attribs
|
||||
aumid
|
||||
Aut
|
||||
@ -136,7 +137,7 @@ Autorun
|
||||
AUTOUPDATE
|
||||
AValid
|
||||
awakeness
|
||||
awakeversion
|
||||
AWAYMODE
|
||||
AYUV
|
||||
azcli
|
||||
azman
|
||||
@ -819,6 +820,8 @@ hhk
|
||||
HHmmss
|
||||
HHOOK
|
||||
hhx
|
||||
Hiber
|
||||
Hiberboot
|
||||
HIBYTE
|
||||
hicon
|
||||
HIDEWINDOW
|
||||
@ -1098,8 +1101,6 @@ LOCALSYSTEM
|
||||
LOCATIONCHANGE
|
||||
LOCKBYTES
|
||||
LOCKTYPE
|
||||
logconsole
|
||||
logfile
|
||||
LOGFONT
|
||||
LOGFONTW
|
||||
logon
|
||||
@ -1134,6 +1135,7 @@ lpsz
|
||||
lpt
|
||||
LPTHREAD
|
||||
LPTOP
|
||||
lptpm
|
||||
LPTSTR
|
||||
LPVOID
|
||||
LPW
|
||||
@ -1193,6 +1195,7 @@ mediatype
|
||||
mef
|
||||
Mega
|
||||
Melman
|
||||
MENUBREAK
|
||||
MENUITEMINFO
|
||||
MENUITEMINFOW
|
||||
MERGECOPY
|
||||
@ -1222,7 +1225,6 @@ MINIMIZEBOX
|
||||
MINIMIZEEND
|
||||
MINIMIZESTART
|
||||
miniz
|
||||
minlevel
|
||||
MINORVERSION
|
||||
Miracast
|
||||
mjpg
|
||||
@ -1334,8 +1336,9 @@ Newtonsoft
|
||||
niels
|
||||
nielslaute
|
||||
NIF
|
||||
nint
|
||||
NLD
|
||||
nlog
|
||||
NLog
|
||||
nls
|
||||
NLSTEXT
|
||||
NOACTIVATE
|
||||
@ -1394,6 +1397,7 @@ ntdll
|
||||
ntfs
|
||||
NTSTATUS
|
||||
nugets
|
||||
nuint
|
||||
nullonfailure
|
||||
numberbox
|
||||
NUMLOCK
|
||||
@ -1450,6 +1454,7 @@ OVERLAPPEDWINDOW
|
||||
overlaywindow
|
||||
Oversampling
|
||||
OWNDC
|
||||
OWNERDRAW
|
||||
Packagemanager
|
||||
PACL
|
||||
PAINTSTRUCT
|
||||
@ -1508,7 +1513,6 @@ pgsql
|
||||
pguid
|
||||
pkey
|
||||
PHANDLE
|
||||
PHANDLER
|
||||
phbm
|
||||
phbmp
|
||||
phwnd
|
||||
@ -1908,7 +1912,6 @@ spdisp
|
||||
spdlog
|
||||
spdo
|
||||
spec'ing
|
||||
specialfolder
|
||||
spesi
|
||||
splitwstring
|
||||
spsi
|
||||
|
@ -25,7 +25,7 @@
|
||||
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
|
||||
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.1722.45" />
|
||||
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta" />
|
||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.0.1" />
|
||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.0.2" />
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.3.230502000" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
|
||||
@ -40,7 +40,7 @@
|
||||
<PackageVersion Include="NLog.Schema" Version="5.0.4" />
|
||||
<PackageVersion Include="ScipBe.Common.Office.OneNote" Version="3.0.1" />
|
||||
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.435" />
|
||||
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta1.20071.2" />
|
||||
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageVersion Include="System.ComponentModel.Composition" Version="7.0.0" />
|
||||
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
|
||||
<PackageVersion Include="System.Data.OleDb" Version="7.0.0" />
|
||||
@ -48,7 +48,7 @@
|
||||
<PackageVersion Include="System.IO.Abstractions" Version="17.2.3" />
|
||||
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="17.2.3" />
|
||||
<PackageVersion Include="System.Management" Version="7.0.0" />
|
||||
<PackageVersion Include="System.Reactive" Version="5.0.0" />
|
||||
<PackageVersion Include="System.Reactive" Version="6.0.0-preview.9" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="7.0.0" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="7.0.0" />
|
||||
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
|
||||
|
@ -300,7 +300,7 @@ SOFTWARE.
|
||||
- Microsoft.Toolkit.Uwp.Notifications 7.1.2
|
||||
- Microsoft.Web.WebView2 1.0.1722.45
|
||||
- Microsoft.Windows.CsWin32 0.2.46-beta
|
||||
- Microsoft.Windows.CsWinRT 2.0.1
|
||||
- Microsoft.Windows.CsWinRT 2.0.2
|
||||
- Microsoft.Windows.SDK.BuildTools 10.0.22621.755
|
||||
- Microsoft.WindowsAppSDK 1.3.230502000
|
||||
- Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9
|
||||
@ -309,12 +309,11 @@ SOFTWARE.
|
||||
- Moq 4.18.3
|
||||
- MSTest.TestAdapter 3.0.1
|
||||
- MSTest.TestFramework 3.0.1
|
||||
- NLog 5.0.4
|
||||
- NLog.Extensions.Logging 5.0.4
|
||||
- NLog.Schema 5.0.4
|
||||
- ScipBe.Common.Office.OneNote 3.0.1
|
||||
- StyleCop.Analyzers 1.2.0-beta.435
|
||||
- System.CommandLine 2.0.0-beta1.20071.2
|
||||
- System.CommandLine 2.0.0-beta4.22272.1
|
||||
- System.ComponentModel.Composition 7.0.0
|
||||
- System.Configuration.ConfigurationManager 6.0.0
|
||||
- System.Data.OleDb 7.0.0
|
||||
@ -322,7 +321,7 @@ SOFTWARE.
|
||||
- System.IO.Abstractions 17.2.3
|
||||
- System.IO.Abstractions.TestingHelpers 17.2.3
|
||||
- System.Management 7.0.0
|
||||
- System.Reactive 5.0.0
|
||||
- System.Reactive 6.0.0-preview.9
|
||||
- System.Runtime.Caching 7.0.0
|
||||
- System.ServiceProcess.ServiceController 7.0.0
|
||||
- UnicodeInformation 2.6.0
|
||||
|
@ -6,13 +6,23 @@ last-update: 3-20-2022
|
||||
|
||||
## Builds
|
||||
|
||||
The build ID can be found in [`NLog.config`](https://github.com/microsoft/PowerToys/blob/2e3a2b3f96f67c7dfc72963e5135662d3230b5fe/src/modules/awake/Awake/NLog.config#L5) - it is a unique identifier for the current builds that allows better diagnostics (we can look up the build ID from the logs) and offers a way to triage Awake-specific issues faster independent of the PowerToys version. The build ID does not carry any significance beyond that within the PowerToys code base.
|
||||
The build ID can be found in `Program.cs` in the `BuildId` variable - it is a unique identifier for the current builds that allows better diagnostics (we can look up the build ID from the logs) and offers a way to triage Awake-specific issues faster independent of the PowerToys version. The build ID does not carry any significance beyond that within the PowerToys code base.
|
||||
|
||||
The build ID moniker is made up of two components - a reference to a [Halo](https://en.wikipedia.org/wiki/Halo_(franchise)) character, and the date when the work on the specific build started in the format of `MMDDYYYY`.
|
||||
|
||||
| Build ID | Build Date |
|
||||
|:----------------------------------------------------------|:-----------------|
|
||||
| [`ATRIOX_04132023`](#ATRIOX_04132023-april-13-2023) | April 13, 2023 |
|
||||
| [`LIBRARIAN_03202022`](#librarian_03202022-march-20-2022) | March 20, 2022 |
|
||||
| `ARBITER_01312022` | January 31, 2022 |
|
||||
|
||||
### `ATRIOX_04132023` (April 13, 2023)
|
||||
|
||||
- Moves from using `Task.Run` to spin up threads to actually using a blocking queue that properly sets thread parameters on the same thread.
|
||||
- Moves back to using native Windows APIs through P/Invoke instead of using a package.
|
||||
- Move away from custom logging and to built-in logging that is consistent with the rest of PowerToys.
|
||||
- Updates `System.CommandLine` and `System.Reactive` to the latest preview versions of the package.
|
||||
|
||||
### `LIBRARIAN_03202022` (March 20, 2022)
|
||||
|
||||
- Changed the tray context menu to be following OS conventions instead of the style offered by Windows Forms. This introduces better support for DPI scaling and theming in the future.
|
||||
|
@ -49,7 +49,7 @@ if ($isWinAppSdkProj -eq $True) {
|
||||
|
||||
$fileExclusionList = @("*Test*", "*.pdb", "*.lastcodeanalysissucceeded", "createdump.exe") + $interopFilesList + $winAppSDKfilesList
|
||||
|
||||
$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*png", "*gif", "*ico", "*cur", "*svg", "index.html", "reg.js", "monacoSpecialLanguages.js", "resources.pri", "NLog.config")
|
||||
$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*png", "*gif", "*ico", "*cur", "*svg", "index.html", "reg.js", "monacoSpecialLanguages.js", "resources.pri")
|
||||
|
||||
$dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll")
|
||||
|
||||
|
@ -5,10 +5,8 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using interop;
|
||||
|
||||
namespace ManagedCommon
|
||||
|
@ -18,6 +18,7 @@
|
||||
<PackageProjectUrl>https://awake.den.dev</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/microsoft/powertoys</RepositoryUrl>
|
||||
<SelfContained>true</SelfContained>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
|
||||
@ -51,16 +52,16 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" />
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Images\Awake.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Windows.CsWinRT" />
|
||||
<PackageReference Include="NLog" />
|
||||
<PackageReference Include="System.CommandLine" />
|
||||
<PackageReference Include="System.Reactive" />
|
||||
<PackageReference Include="System.Runtime.Caching" />
|
||||
@ -79,16 +80,17 @@
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="NLog.config">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Images\Awake.ico">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.CodeAnalysis.NetAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -1,394 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.Win32;
|
||||
using NLog;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Storage.FileSystem;
|
||||
using Windows.Win32.System.Console;
|
||||
using Windows.Win32.System.Power;
|
||||
|
||||
namespace Awake.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class that allows talking to Win32 APIs without having to rely on PInvoke in other parts
|
||||
/// of the codebase.
|
||||
/// </summary>
|
||||
public class APIHelper
|
||||
{
|
||||
private const string BuildRegistryLocation = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
|
||||
|
||||
private static readonly Logger _log;
|
||||
private static CancellationTokenSource _tokenSource;
|
||||
private static CancellationToken _threadToken;
|
||||
|
||||
private static Task? _runnerThread;
|
||||
private static System.Timers.Timer _timedLoopTimer;
|
||||
|
||||
static APIHelper()
|
||||
{
|
||||
_timedLoopTimer = new System.Timers.Timer();
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
internal static void SetConsoleControlHandler(PHANDLER_ROUTINE handler, bool addHandler)
|
||||
{
|
||||
PInvoke.SetConsoleCtrlHandler(handler, addHandler);
|
||||
}
|
||||
|
||||
public static void AllocateConsole()
|
||||
{
|
||||
_log.Debug("Bootstrapping the console allocation routine.");
|
||||
PInvoke.AllocConsole();
|
||||
_log.Debug($"Console allocation result: {Marshal.GetLastWin32Error()}");
|
||||
|
||||
var outputFilePointer = PInvoke.CreateFile("CONOUT$", FILE_ACCESS_FLAGS.FILE_GENERIC_READ | FILE_ACCESS_FLAGS.FILE_GENERIC_WRITE, FILE_SHARE_MODE.FILE_SHARE_WRITE, null, FILE_CREATION_DISPOSITION.OPEN_EXISTING, 0, null);
|
||||
_log.Debug($"CONOUT creation result: {Marshal.GetLastWin32Error()}");
|
||||
|
||||
PInvoke.SetStdHandle(Windows.Win32.System.Console.STD_HANDLE.STD_OUTPUT_HANDLE, outputFilePointer);
|
||||
_log.Debug($"SetStdHandle result: {Marshal.GetLastWin32Error()}");
|
||||
|
||||
Console.SetOut(new StreamWriter(Console.OpenStandardOutput(), Console.OutputEncoding) { AutoFlush = true });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the computer awake state using the native Win32 SetThreadExecutionState API. This
|
||||
/// function is just a nice-to-have wrapper that helps avoid tracking the success or failure of
|
||||
/// the call.
|
||||
/// </summary>
|
||||
/// <param name="state">Single or multiple EXECUTION_STATE entries.</param>
|
||||
/// <returns>true if successful, false if failed</returns>
|
||||
private static bool SetAwakeState(EXECUTION_STATE state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var stateResult = PInvoke.SetThreadExecutionState(state);
|
||||
return stateResult != 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool SetAwakeStateBasedOnDisplaySetting(bool keepDisplayOn)
|
||||
{
|
||||
if (keepDisplayOn)
|
||||
{
|
||||
return SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
|
||||
}
|
||||
}
|
||||
|
||||
public static void CancelExistingThread()
|
||||
{
|
||||
_tokenSource.Cancel();
|
||||
|
||||
try
|
||||
{
|
||||
_log.Info("Attempting to ensure that the thread is properly cleaned up...");
|
||||
|
||||
if (_runnerThread != null && !_runnerThread.IsCanceled)
|
||||
{
|
||||
_runnerThread.Wait(_threadToken);
|
||||
}
|
||||
|
||||
_log.Info("Thread is clean.");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_log.Info("Confirmed background thread cancellation when disabling explicit keep awake.");
|
||||
}
|
||||
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
_threadToken = _tokenSource.Token;
|
||||
|
||||
_log.Info("Instantiating of new token source and thread token completed.");
|
||||
}
|
||||
|
||||
public static void SetIndefiniteKeepAwake(Action callback, Action failureCallback, bool keepDisplayOn = false)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new Awake.Telemetry.AwakeIndefinitelyKeepAwakeEvent());
|
||||
|
||||
CancelExistingThread();
|
||||
|
||||
try
|
||||
{
|
||||
_runnerThread = Task.Run(() => RunIndefiniteJob(keepDisplayOn), _threadToken)
|
||||
.ContinueWith((result) => callback, TaskContinuationOptions.OnlyOnRanToCompletion)
|
||||
.ContinueWith((result) => failureCallback, TaskContinuationOptions.NotOnRanToCompletion);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetNoKeepAwake()
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new Awake.Telemetry.AwakeNoKeepAwakeEvent());
|
||||
|
||||
CancelExistingThread();
|
||||
}
|
||||
|
||||
public static void SetExpirableKeepAwake(DateTimeOffset expireAt, Action callback, Action failureCallback, bool keepDisplayOn = true)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new Awake.Telemetry.AwakeExpirableKeepAwakeEvent());
|
||||
|
||||
CancelExistingThread();
|
||||
|
||||
if (expireAt > DateTime.Now && expireAt != null)
|
||||
{
|
||||
_runnerThread = Task.Run(() => RunExpiringJob(expireAt, keepDisplayOn), _threadToken)
|
||||
.ContinueWith((result) => callback, TaskContinuationOptions.OnlyOnRanToCompletion)
|
||||
.ContinueWith((result) => failureCallback, TaskContinuationOptions.NotOnRanToCompletion);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The target date is not in the future.
|
||||
_log.Error("The specified target date and time is not in the future.");
|
||||
_log.Error($"Current time: {DateTime.Now}\tTarget time: {expireAt}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetTimedKeepAwake(uint seconds, Action callback, Action failureCallback, bool keepDisplayOn = true)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new Awake.Telemetry.AwakeTimedKeepAwakeEvent());
|
||||
|
||||
CancelExistingThread();
|
||||
|
||||
_runnerThread = Task.Run(() => RunTimedJob(seconds, keepDisplayOn), _threadToken)
|
||||
.ContinueWith((result) => callback, TaskContinuationOptions.OnlyOnRanToCompletion)
|
||||
.ContinueWith((result) => failureCallback, TaskContinuationOptions.NotOnRanToCompletion);
|
||||
}
|
||||
|
||||
private static void RunExpiringJob(DateTimeOffset expireAt, bool keepDisplayOn = false)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
// In case cancellation was already requested.
|
||||
_threadToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
success = SetAwakeStateBasedOnDisplaySetting(keepDisplayOn);
|
||||
|
||||
if (success)
|
||||
{
|
||||
_log.Info($"Initiated expirable keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
|
||||
|
||||
Observable.Timer(expireAt, Scheduler.CurrentThread).Subscribe(
|
||||
_ =>
|
||||
{
|
||||
_log.Info($"Completed expirable thread in {PInvoke.GetCurrentThreadId()}.");
|
||||
CancelExistingThread();
|
||||
},
|
||||
_tokenSource.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
_log.Info("Could not successfully set up expirable keep awake.");
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
// Task was clearly cancelled.
|
||||
_log.Info($"Background thread termination: {PInvoke.GetCurrentThreadId()}. Message: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunIndefiniteJob(bool keepDisplayOn = false)
|
||||
{
|
||||
// In case cancellation was already requested.
|
||||
_threadToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
bool success = SetAwakeStateBasedOnDisplaySetting(keepDisplayOn);
|
||||
|
||||
if (success)
|
||||
{
|
||||
_log.Info($"Initiated indefinite keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
|
||||
|
||||
WaitHandle.WaitAny(new[] { _threadToken.WaitHandle });
|
||||
}
|
||||
else
|
||||
{
|
||||
_log.Info("Could not successfully set up indefinite keep awake.");
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
// Task was clearly cancelled.
|
||||
_log.Info($"Background thread termination: {PInvoke.GetCurrentThreadId()}. Message: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CompleteExit(int exitCode, ManualResetEvent? exitSignal, bool force = false)
|
||||
{
|
||||
SetNoKeepAwake();
|
||||
|
||||
HWND windowHandle = GetHiddenWindow();
|
||||
|
||||
if (windowHandle != HWND.Null)
|
||||
{
|
||||
PInvoke.SendMessage(windowHandle, PInvoke.WM_CLOSE, 0, 0);
|
||||
}
|
||||
|
||||
if (force)
|
||||
{
|
||||
PInvoke.PostQuitMessage(0);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
exitSignal?.Set();
|
||||
PInvoke.DestroyWindow(windowHandle);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Info($"Exit signal error ${ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunTimedJob(uint seconds, bool keepDisplayOn = true)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
// In case cancellation was already requested.
|
||||
_threadToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
success = SetAwakeStateBasedOnDisplaySetting(keepDisplayOn);
|
||||
|
||||
if (success)
|
||||
{
|
||||
_log.Info($"Initiated timed keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
|
||||
|
||||
Observable.Timer(TimeSpan.FromSeconds(seconds), Scheduler.CurrentThread).Subscribe(
|
||||
_ =>
|
||||
{
|
||||
_log.Info($"Completed timed thread in {PInvoke.GetCurrentThreadId()}.");
|
||||
CancelExistingThread();
|
||||
},
|
||||
_tokenSource.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
_log.Info("Could not set up timed keep-awake with display on.");
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
// Task was clearly cancelled.
|
||||
_log.Info($"Background thread termination: {PInvoke.GetCurrentThreadId()}. Message: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetOperatingSystemBuild()
|
||||
{
|
||||
try
|
||||
{
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(BuildRegistryLocation);
|
||||
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
|
||||
if (registryKey != null)
|
||||
{
|
||||
var versionString = $"{registryKey.GetValue("ProductName")} {registryKey.GetValue("DisplayVersion")} {registryKey.GetValue("BuildLabEx")}";
|
||||
return versionString;
|
||||
}
|
||||
else
|
||||
{
|
||||
_log.Info("Registry key acquisition for OS failed.");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Info($"Could not get registry key for the build number. Error: {ex.Message}");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Performance", "CA1806:Do not ignore method results", Justification = "Function returns DWORD value that identifies the current thread, but we do not need it.")]
|
||||
internal static IEnumerable<HWND> EnumerateWindowsForProcess(int processId)
|
||||
{
|
||||
var handles = new List<HWND>();
|
||||
var hCurrentWnd = HWND.Null;
|
||||
|
||||
do
|
||||
{
|
||||
hCurrentWnd = PInvoke.FindWindowEx(HWND.Null, hCurrentWnd, null as string, null);
|
||||
uint targetProcessId = 0;
|
||||
unsafe
|
||||
{
|
||||
PInvoke.GetWindowThreadProcessId(hCurrentWnd, &targetProcessId);
|
||||
}
|
||||
|
||||
if (targetProcessId == processId)
|
||||
{
|
||||
handles.Add(hCurrentWnd);
|
||||
}
|
||||
}
|
||||
while (hCurrentWnd != IntPtr.Zero);
|
||||
|
||||
return handles;
|
||||
}
|
||||
|
||||
[SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "In this context, the string is only converted to a hex value.")]
|
||||
internal static HWND GetHiddenWindow()
|
||||
{
|
||||
IEnumerable<HWND> windowHandles = EnumerateWindowsForProcess(Environment.ProcessId);
|
||||
var domain = AppDomain.CurrentDomain.GetHashCode().ToString("x");
|
||||
string targetClass = $"{InternalConstants.TrayWindowId}{domain}";
|
||||
|
||||
unsafe
|
||||
{
|
||||
var classNameLen = 256;
|
||||
Span<char> className = stackalloc char[classNameLen];
|
||||
foreach (var handle in windowHandles)
|
||||
{
|
||||
fixed (char* ptr = className)
|
||||
{
|
||||
int classQueryResult = PInvoke.GetClassName(handle, ptr, classNameLen);
|
||||
if (classQueryResult != 0 && className.ToString().StartsWith(targetClass, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HWND.Null;
|
||||
}
|
||||
|
||||
public static Dictionary<string, int> GetDefaultTrayOptions()
|
||||
{
|
||||
Dictionary<string, int> optionsList = new Dictionary<string, int>
|
||||
{
|
||||
{ "30 minutes", 1800 },
|
||||
{ "1 hour", 3600 },
|
||||
{ "2 hours", 7200 },
|
||||
};
|
||||
return optionsList;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,10 +4,11 @@
|
||||
|
||||
namespace Awake.Core
|
||||
{
|
||||
internal static class InternalConstants
|
||||
internal static class Constants
|
||||
{
|
||||
internal const string AppName = "Awake";
|
||||
internal const string FullAppName = "PowerToys " + AppName;
|
||||
internal const string TrayWindowId = "WindowsForms10.Window.0.app.0.";
|
||||
internal const string BuildRegistryLocation = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
|
||||
}
|
||||
}
|
284
src/modules/awake/Awake/Core/Manager.cs
Normal file
284
src/modules/awake/Awake/Core/Manager.cs
Normal file
@ -0,0 +1,284 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Awake.Core.Models;
|
||||
using Awake.Core.Native;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Awake.Core
|
||||
{
|
||||
public delegate bool ConsoleEventHandler(Models.ControlType ctrlType);
|
||||
|
||||
/// <summary>
|
||||
/// Helper class that allows talking to Win32 APIs without having to rely on PInvoke in other parts
|
||||
/// of the codebase.
|
||||
/// </summary>
|
||||
public class Manager
|
||||
{
|
||||
private static BlockingCollection<ExecutionState> _stateQueue;
|
||||
|
||||
private static CancellationTokenSource _tokenSource;
|
||||
|
||||
static Manager()
|
||||
{
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
_stateQueue = new BlockingCollection<ExecutionState>();
|
||||
}
|
||||
|
||||
public static void StartMonitor()
|
||||
{
|
||||
Thread monitorThread = new(() =>
|
||||
{
|
||||
Thread.CurrentThread.IsBackground = true;
|
||||
while (true)
|
||||
{
|
||||
ExecutionState state = _stateQueue.Take();
|
||||
|
||||
Logger.LogInfo($"Setting state to {state}");
|
||||
|
||||
SetAwakeState(state);
|
||||
}
|
||||
});
|
||||
monitorThread.Start();
|
||||
}
|
||||
|
||||
internal static void SetConsoleControlHandler(ConsoleEventHandler handler, bool addHandler)
|
||||
{
|
||||
Bridge.SetConsoleCtrlHandler(handler, addHandler);
|
||||
}
|
||||
|
||||
public static void AllocateConsole()
|
||||
{
|
||||
Bridge.AllocConsole();
|
||||
|
||||
var outputFilePointer = Bridge.CreateFile("CONOUT$", Native.Constants.GENERIC_READ | Native.Constants.GENERIC_WRITE, FileShare.Write, IntPtr.Zero, FileMode.OpenOrCreate, 0, IntPtr.Zero);
|
||||
|
||||
Bridge.SetStdHandle(Native.Constants.STD_OUTPUT_HANDLE, outputFilePointer);
|
||||
|
||||
Console.SetOut(new StreamWriter(Console.OpenStandardOutput(), Console.OutputEncoding) { AutoFlush = true });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the computer awake state using the native Win32 SetThreadExecutionState API. This
|
||||
/// function is just a nice-to-have wrapper that helps avoid tracking the success or failure of
|
||||
/// the call.
|
||||
/// </summary>
|
||||
/// <param name="state">Single or multiple EXECUTION_STATE entries.</param>
|
||||
/// <returns>true if successful, false if failed</returns>
|
||||
private static bool SetAwakeState(ExecutionState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var stateResult = Bridge.SetThreadExecutionState(state);
|
||||
return stateResult != 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static ExecutionState ComputeAwakeState(bool keepDisplayOn)
|
||||
{
|
||||
if (keepDisplayOn)
|
||||
{
|
||||
return ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_DISPLAY_REQUIRED | ExecutionState.ES_CONTINUOUS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_CONTINUOUS;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CancelExistingThread()
|
||||
{
|
||||
Logger.LogInfo($"Attempting to ensure that the thread is properly cleaned up...");
|
||||
|
||||
// Resetting the thread state.
|
||||
_stateQueue.Add(ExecutionState.ES_CONTINUOUS);
|
||||
|
||||
// Next, make sure that any existing background threads are terminated.
|
||||
_tokenSource.Cancel();
|
||||
_tokenSource.Dispose();
|
||||
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
Logger.LogInfo("Instantiating of new token source and thread token completed.");
|
||||
}
|
||||
|
||||
public static void SetIndefiniteKeepAwake(bool keepDisplayOn = false)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AwakeIndefinitelyKeepAwakeEvent());
|
||||
|
||||
CancelExistingThread();
|
||||
|
||||
_stateQueue.Add(ComputeAwakeState(keepDisplayOn));
|
||||
}
|
||||
|
||||
public static void SetNoKeepAwake()
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AwakeNoKeepAwakeEvent());
|
||||
|
||||
CancelExistingThread();
|
||||
}
|
||||
|
||||
public static void SetExpirableKeepAwake(DateTimeOffset expireAt, bool keepDisplayOn = true)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AwakeExpirableKeepAwakeEvent());
|
||||
|
||||
CancelExistingThread();
|
||||
|
||||
if (expireAt > DateTime.Now && expireAt != null)
|
||||
{
|
||||
Logger.LogInfo($"Starting expirable log for {expireAt}");
|
||||
_stateQueue.Add(ComputeAwakeState(keepDisplayOn));
|
||||
|
||||
Observable.Timer(expireAt).Subscribe(
|
||||
_ =>
|
||||
{
|
||||
Logger.LogInfo($"Completed expirable keep-awake.");
|
||||
CancelExistingThread();
|
||||
},
|
||||
_tokenSource.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The target date is not in the future.
|
||||
Logger.LogError("The specified target date and time is not in the future.");
|
||||
Logger.LogError($"Current time: {DateTime.Now}\tTarget time: {expireAt}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetTimedKeepAwake(uint seconds, bool keepDisplayOn = true)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AwakeTimedKeepAwakeEvent());
|
||||
|
||||
CancelExistingThread();
|
||||
|
||||
Logger.LogInfo($"Timed keep awake started for {seconds} seconds.");
|
||||
|
||||
_stateQueue.Add(ComputeAwakeState(keepDisplayOn));
|
||||
|
||||
Observable.Timer(TimeSpan.FromSeconds(seconds)).Subscribe(
|
||||
_ =>
|
||||
{
|
||||
Logger.LogInfo($"Completed timed thread.");
|
||||
CancelExistingThread();
|
||||
},
|
||||
_tokenSource.Token);
|
||||
}
|
||||
|
||||
internal static void CompleteExit(int exitCode, ManualResetEvent? exitSignal, bool force = false)
|
||||
{
|
||||
SetNoKeepAwake();
|
||||
|
||||
IntPtr windowHandle = GetHiddenWindow();
|
||||
|
||||
if (windowHandle != IntPtr.Zero)
|
||||
{
|
||||
Bridge.SendMessage(windowHandle, Native.Constants.WM_CLOSE, 0, 0);
|
||||
}
|
||||
|
||||
if (force)
|
||||
{
|
||||
Bridge.PostQuitMessage(exitCode);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
exitSignal?.Set();
|
||||
Bridge.DestroyWindow(windowHandle);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Exit signal error ${ex}");
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetOperatingSystemBuild()
|
||||
{
|
||||
try
|
||||
{
|
||||
RegistryKey? registryKey = Registry.LocalMachine.OpenSubKey(Constants.BuildRegistryLocation);
|
||||
|
||||
if (registryKey != null)
|
||||
{
|
||||
var versionString = $"{registryKey.GetValue("ProductName")} {registryKey.GetValue("DisplayVersion")} {registryKey.GetValue("BuildLabEx")}";
|
||||
return versionString;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogError("Registry key acquisition for OS failed.");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Could not get registry key for the build number. Error: {ex.Message}");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Performance", "CA1806:Do not ignore method results", Justification = "Function returns DWORD value that identifies the current thread, but we do not need it.")]
|
||||
internal static IEnumerable<IntPtr> EnumerateWindowsForProcess(int processId)
|
||||
{
|
||||
var handles = new List<IntPtr>();
|
||||
var hCurrentWnd = IntPtr.Zero;
|
||||
|
||||
do
|
||||
{
|
||||
hCurrentWnd = Bridge.FindWindowEx(IntPtr.Zero, hCurrentWnd, null as string, null);
|
||||
Bridge.GetWindowThreadProcessId(hCurrentWnd, out uint targetProcessId);
|
||||
|
||||
if (targetProcessId == processId)
|
||||
{
|
||||
handles.Add(hCurrentWnd);
|
||||
}
|
||||
}
|
||||
while (hCurrentWnd != IntPtr.Zero);
|
||||
|
||||
return handles;
|
||||
}
|
||||
|
||||
[SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "In this context, the string is only converted to a hex value.")]
|
||||
internal static IntPtr GetHiddenWindow()
|
||||
{
|
||||
IEnumerable<IntPtr> windowHandles = EnumerateWindowsForProcess(Environment.ProcessId);
|
||||
var domain = AppDomain.CurrentDomain.GetHashCode().ToString("x");
|
||||
string targetClass = $"{Constants.TrayWindowId}{domain}";
|
||||
|
||||
foreach (var handle in windowHandles)
|
||||
{
|
||||
StringBuilder className = new(256);
|
||||
int classQueryResult = Bridge.GetClassName(handle, className, className.Capacity);
|
||||
if (classQueryResult != 0 && className.ToString().StartsWith(targetClass, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
public static Dictionary<string, int> GetDefaultTrayOptions()
|
||||
{
|
||||
Dictionary<string, int> optionsList = new Dictionary<string, int>
|
||||
{
|
||||
{ "30 minutes", 1800 },
|
||||
{ "1 hour", 3600 },
|
||||
{ "2 hours", 7200 },
|
||||
};
|
||||
return optionsList;
|
||||
}
|
||||
}
|
||||
}
|
12
src/modules/awake/Awake/Core/Models/BatteryReportingScale.cs
Normal file
12
src/modules/awake/Awake/Core/Models/BatteryReportingScale.cs
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Awake.Core.Models
|
||||
{
|
||||
public struct BatteryReportingScale
|
||||
{
|
||||
public uint Granularity;
|
||||
public uint Capacity;
|
||||
}
|
||||
}
|
21
src/modules/awake/Awake/Core/Models/ControlType.cs
Normal file
21
src/modules/awake/Awake/Core/Models/ControlType.cs
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Awake.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of control signal received by the handler.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See <see href="https://learn.microsoft.com/windows/console/handlerroutine">HandlerRoutine callback function</see>.
|
||||
/// </remarks>
|
||||
public enum ControlType
|
||||
{
|
||||
CTRL_C_EVENT = 0,
|
||||
CTRL_BREAK_EVENT = 1,
|
||||
CTRL_CLOSE_EVENT = 2,
|
||||
CTRL_LOGOFF_EVENT = 5,
|
||||
CTRL_SHUTDOWN_EVENT = 6,
|
||||
}
|
||||
}
|
17
src/modules/awake/Awake/Core/Models/ExecutionState.cs
Normal file
17
src/modules/awake/Awake/Core/Models/ExecutionState.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Awake.Core.Models
|
||||
{
|
||||
[Flags]
|
||||
public enum ExecutionState : uint
|
||||
{
|
||||
ES_AWAYMODE_REQUIRED = 0x00000040,
|
||||
ES_CONTINUOUS = 0x80000000,
|
||||
ES_DISPLAY_REQUIRED = 0x00000002,
|
||||
ES_SYSTEM_REQUIRED = 0x00000001,
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Awake.Core.Models
|
||||
{
|
||||
public struct SystemPowerCapabilities
|
||||
{
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool PowerButtonPresent;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool SleepButtonPresent;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool LidPresent;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool SystemS1;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool SystemS2;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool SystemS3;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool SystemS4;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool SystemS5;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool HiberFilePresent;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool FullWake;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool VideoDimPresent;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool ApmPresent;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool UpsPresent;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool ThermalControl;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool ProcessorThrottle;
|
||||
public byte ProcessorMinThrottle;
|
||||
public byte ProcessorMaxThrottle;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool FastSystemS4;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool Hiberboot;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool WakeAlarmPresent;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool AoAc;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool DiskSpinDown;
|
||||
public byte HiberFileType;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool AoAcConnectivitySupported;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
|
||||
private readonly byte[] spare3;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool SystemBatteriesPresent;
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool BatteriesAreShortTerm;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
||||
public BatteryReportingScale[] BatteryScale;
|
||||
public SystemPowerState AcOnLineWake;
|
||||
public SystemPowerState SoftLidWake;
|
||||
public SystemPowerState RtcWake;
|
||||
public SystemPowerState MinDeviceWakeState;
|
||||
public SystemPowerState DefaultLowLatencyWake;
|
||||
}
|
||||
}
|
24
src/modules/awake/Awake/Core/Models/SystemPowerState.cs
Normal file
24
src/modules/awake/Awake/Core/Models/SystemPowerState.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Awake.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the system power state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See <see href="https://learn.microsoft.com/windows/win32/power/system-power-states">System power states</see>.
|
||||
/// </remarks>
|
||||
public enum SystemPowerState
|
||||
{
|
||||
PowerSystemUnspecified = 0,
|
||||
PowerSystemWorking = 1,
|
||||
PowerSystemSleeping1 = 2,
|
||||
PowerSystemSleeping2 = 3,
|
||||
PowerSystemSleeping3 = 4,
|
||||
PowerSystemHibernate = 5,
|
||||
PowerSystemShutdown = 6,
|
||||
PowerSystemMaximum = 7,
|
||||
}
|
||||
}
|
@ -2,17 +2,15 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.Win32;
|
||||
|
||||
namespace Awake.Core.Models
|
||||
{
|
||||
internal enum TrayCommands : uint
|
||||
{
|
||||
TC_DISPLAY_SETTING = PInvoke.WM_USER + 1,
|
||||
TC_MODE_PASSIVE = PInvoke.WM_USER + 2,
|
||||
TC_MODE_INDEFINITE = PInvoke.WM_USER + 3,
|
||||
TC_MODE_EXPIRABLE = PInvoke.WM_USER + 4,
|
||||
TC_EXIT = PInvoke.WM_USER + 100,
|
||||
TC_TIME = PInvoke.WM_USER + 101,
|
||||
TC_DISPLAY_SETTING = Native.Constants.WM_USER + 1,
|
||||
TC_MODE_PASSIVE = Native.Constants.WM_USER + 2,
|
||||
TC_MODE_INDEFINITE = Native.Constants.WM_USER + 3,
|
||||
TC_MODE_EXPIRABLE = Native.Constants.WM_USER + 4,
|
||||
TC_EXIT = Native.Constants.WM_USER + 100,
|
||||
TC_TIME = Native.Constants.WM_USER + 101,
|
||||
}
|
||||
}
|
||||
|
80
src/modules/awake/Awake/Core/Native/Bridge.cs
Normal file
80
src/modules/awake/Awake/Core/Native/Bridge.cs
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Awake.Core.Models;
|
||||
|
||||
namespace Awake.Core.Native
|
||||
{
|
||||
internal sealed class Bridge
|
||||
{
|
||||
internal delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
|
||||
|
||||
[DllImport("Powrprof.dll", SetLastError = true)]
|
||||
internal static extern bool GetPwrCapabilities(out SystemPowerCapabilities lpSystemPowerCapabilities);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool SetConsoleCtrlHandler(ConsoleEventHandler handler, bool add);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
internal static extern ExecutionState SetThreadExecutionState(ExecutionState esFlags);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool AllocConsole();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern uint GetCurrentThreadId();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
internal static extern IntPtr CreateFile(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string filename,
|
||||
[MarshalAs(UnmanagedType.U4)] uint access,
|
||||
[MarshalAs(UnmanagedType.U4)] FileShare share,
|
||||
IntPtr securityAttributes,
|
||||
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
|
||||
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
|
||||
IntPtr templateFile);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr CreatePopupMenu();
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
internal static extern bool InsertMenu(IntPtr hMenu, uint uPosition, uint uFlags, uint uIDNewItem, string lpNewItem);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool TrackPopupMenuEx(IntPtr hmenu, uint fuFlags, int x, int y, IntPtr hwnd, IntPtr lptpm);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
internal static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr hWndChildAfter, string? className, string? windowTitle);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
internal static extern IntPtr SendMessage(IntPtr hWnd, uint msg, nuint wParam, nint lParam);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool DestroyMenu(IntPtr hMenu);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern bool DestroyWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern void PostQuitMessage(int nExitCode);
|
||||
}
|
||||
}
|
32
src/modules/awake/Awake/Core/Native/Constants.cs
Normal file
32
src/modules/awake/Awake/Core/Native/Constants.cs
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Awake.Core.Native
|
||||
{
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Win32 API convention.")]
|
||||
internal sealed class Constants
|
||||
{
|
||||
internal const uint WM_COMMAND = 0x111;
|
||||
internal const uint WM_USER = 0x400;
|
||||
internal const uint WM_GETTEXT = 0x000D;
|
||||
internal const uint WM_CLOSE = 0x0010;
|
||||
|
||||
// Popup menu constants.
|
||||
internal const uint MF_BYPOSITION = 1024;
|
||||
internal const uint MF_STRING = 0;
|
||||
internal const uint MF_MENUBREAK = 0x00000040;
|
||||
internal const uint MF_SEPARATOR = 0x00000800;
|
||||
internal const uint MF_POPUP = 0x00000010;
|
||||
internal const uint MF_UNCHECKED = 0x00000000;
|
||||
internal const uint MF_CHECKED = 0x00000008;
|
||||
internal const uint MF_OWNERDRAW = 0x00000100;
|
||||
|
||||
internal const uint MF_ENABLED = 0x00000000;
|
||||
internal const uint MF_DISABLED = 0x00000002;
|
||||
|
||||
internal const int STD_OUTPUT_HANDLE = -11;
|
||||
internal const uint GENERIC_WRITE = 0x40000000;
|
||||
internal const uint GENERIC_READ = 0x80000000;
|
||||
}
|
||||
}
|
@ -10,13 +10,9 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
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.UI.WindowsAndMessaging;
|
||||
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
|
||||
namespace Awake.Core
|
||||
{
|
||||
@ -29,16 +25,14 @@ namespace Awake.Core
|
||||
/// </remarks>
|
||||
internal static class TrayHelper
|
||||
{
|
||||
private static readonly Logger _log;
|
||||
private static IntPtr _trayMenu;
|
||||
|
||||
private static DestroyMenuSafeHandle TrayMenu { get; set; }
|
||||
private static IntPtr TrayMenu { get => _trayMenu; set => _trayMenu = value; }
|
||||
|
||||
private static NotifyIcon TrayIcon { get; set; }
|
||||
|
||||
static TrayHelper()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
TrayMenu = new DestroyMenuSafeHandle();
|
||||
TrayIcon = new NotifyIcon();
|
||||
}
|
||||
|
||||
@ -49,20 +43,23 @@ namespace Awake.Core
|
||||
{
|
||||
try
|
||||
{
|
||||
_log.Info("Setting up the tray.");
|
||||
((NotifyIcon?)tray).Text = text;
|
||||
((NotifyIcon?)tray).Icon = icon;
|
||||
((NotifyIcon?)tray).ContextMenuStrip = contextMenu;
|
||||
((NotifyIcon?)tray).Visible = true;
|
||||
((NotifyIcon?)tray).MouseClick += TrayClickHandler;
|
||||
Application.AddMessageFilter(new TrayMessageFilter(exitSignal));
|
||||
Application.Run();
|
||||
_log.Info("Tray setup complete.");
|
||||
Logger.LogInfo("Setting up the tray.");
|
||||
if (tray != null)
|
||||
{
|
||||
((NotifyIcon)tray).Text = text;
|
||||
((NotifyIcon)tray).Icon = icon;
|
||||
((NotifyIcon)tray).ContextMenuStrip = contextMenu;
|
||||
((NotifyIcon)tray).Visible = true;
|
||||
((NotifyIcon)tray).MouseClick += TrayClickHandler;
|
||||
Application.AddMessageFilter(new TrayMessageFilter(exitSignal));
|
||||
Application.Run();
|
||||
Logger.LogInfo("Tray setup complete.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error($"An error occurred initializing the tray. {ex.Message}");
|
||||
_log.Error($"{ex.StackTrace}");
|
||||
Logger.LogError($"An error occurred initializing the tray. {ex.Message}");
|
||||
Logger.LogError($"{ex.StackTrace}");
|
||||
}
|
||||
},
|
||||
TrayIcon);
|
||||
@ -81,12 +78,12 @@ namespace Awake.Core
|
||||
/// <param name="e">MouseEventArgs instance containing mouse click event information.</param>
|
||||
private static void TrayClickHandler(object? sender, MouseEventArgs e)
|
||||
{
|
||||
HWND windowHandle = APIHelper.GetHiddenWindow();
|
||||
IntPtr windowHandle = Manager.GetHiddenWindow();
|
||||
|
||||
if (windowHandle != HWND.Null)
|
||||
if (windowHandle != IntPtr.Zero)
|
||||
{
|
||||
PInvoke.SetForegroundWindow(windowHandle);
|
||||
PInvoke.TrackPopupMenuEx(TrayMenu, 0, Cursor.Position.X, Cursor.Position.Y, windowHandle, null);
|
||||
Bridge.SetForegroundWindow(windowHandle);
|
||||
Bridge.TrackPopupMenuEx(TrayMenu, 0, Cursor.Position.X, Cursor.Position.Y, windowHandle, IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,46 +99,55 @@ namespace Awake.Core
|
||||
|
||||
public static void SetTray(string text, bool keepDisplayOn, AwakeMode mode, Dictionary<string, int> trayTimeShortcuts, bool startedFromPowerToys)
|
||||
{
|
||||
TrayMenu = new DestroyMenuSafeHandle(PInvoke.CreatePopupMenu());
|
||||
if (TrayMenu != IntPtr.Zero)
|
||||
{
|
||||
var destructionStatus = Bridge.DestroyMenu(TrayMenu);
|
||||
if (destructionStatus != true)
|
||||
{
|
||||
Logger.LogError("Failed to destroy menu.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!TrayMenu.IsInvalid)
|
||||
TrayMenu = Bridge.CreatePopupMenu();
|
||||
|
||||
if (TrayMenu != IntPtr.Zero)
|
||||
{
|
||||
if (!startedFromPowerToys)
|
||||
{
|
||||
// If Awake is started from PowerToys, the correct way to exit it is disabling it from Settings.
|
||||
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING, (uint)TrayCommands.TC_EXIT, "Exit");
|
||||
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_SEPARATOR, 0, string.Empty);
|
||||
Bridge.InsertMenu(TrayMenu, 0, Native.Constants.MF_BYPOSITION | Native.Constants.MF_STRING, (uint)TrayCommands.TC_EXIT, "Exit");
|
||||
Bridge.InsertMenu(TrayMenu, 0, Native.Constants.MF_BYPOSITION | Native.Constants.MF_SEPARATOR, 0, string.Empty);
|
||||
}
|
||||
|
||||
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (keepDisplayOn ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED) | (mode == AwakeMode.PASSIVE ? MENU_ITEM_FLAGS.MF_DISABLED : MENU_ITEM_FLAGS.MF_ENABLED), (uint)TrayCommands.TC_DISPLAY_SETTING, "Keep screen on");
|
||||
Bridge.InsertMenu(TrayMenu, 0, Native.Constants.MF_BYPOSITION | Native.Constants.MF_STRING | (keepDisplayOn ? Native.Constants.MF_CHECKED : Native.Constants.MF_UNCHECKED) | (mode == AwakeMode.PASSIVE ? Native.Constants.MF_DISABLED : Native.Constants.MF_ENABLED), (uint)TrayCommands.TC_DISPLAY_SETTING, "Keep screen on");
|
||||
}
|
||||
|
||||
// In case there are no tray shortcuts defined for the application default to a
|
||||
// reasonable initial set.
|
||||
if (trayTimeShortcuts.Count == 0)
|
||||
{
|
||||
trayTimeShortcuts.AddRange(APIHelper.GetDefaultTrayOptions());
|
||||
trayTimeShortcuts.AddRange(Manager.GetDefaultTrayOptions());
|
||||
}
|
||||
|
||||
var awakeTimeMenu = new DestroyMenuSafeHandle(PInvoke.CreatePopupMenu(), false);
|
||||
var awakeTimeMenu = Bridge.CreatePopupMenu();
|
||||
for (int i = 0; i < trayTimeShortcuts.Count; i++)
|
||||
{
|
||||
PInvoke.InsertMenu(awakeTimeMenu, (uint)i, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING, (uint)TrayCommands.TC_TIME + (uint)i, trayTimeShortcuts.ElementAt(i).Key);
|
||||
Bridge.InsertMenu(awakeTimeMenu, (uint)i, Native.Constants.MF_BYPOSITION | Native.Constants.MF_STRING, (uint)TrayCommands.TC_TIME + (uint)i, trayTimeShortcuts.ElementAt(i).Key);
|
||||
}
|
||||
|
||||
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_SEPARATOR, 0, string.Empty);
|
||||
Bridge.InsertMenu(TrayMenu, 0, Native.Constants.MF_BYPOSITION | Native.Constants.MF_SEPARATOR, 0, string.Empty);
|
||||
|
||||
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (mode == AwakeMode.PASSIVE ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_PASSIVE, "Off (keep using the selected power plan)");
|
||||
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (mode == AwakeMode.INDEFINITE ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_INDEFINITE, "Keep awake indefinitely");
|
||||
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_POPUP | (mode == AwakeMode.TIMED ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)awakeTimeMenu.DangerousGetHandle(), "Keep awake on interval");
|
||||
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | MENU_ITEM_FLAGS.MF_DISABLED | (mode == AwakeMode.EXPIRABLE ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_EXPIRABLE, "Keep awake until expiration date and time");
|
||||
Bridge.InsertMenu(TrayMenu, 0, Native.Constants.MF_BYPOSITION | Native.Constants.MF_STRING | (mode == AwakeMode.PASSIVE ? Native.Constants.MF_CHECKED : Native.Constants.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_PASSIVE, "Off (keep using the selected power plan)");
|
||||
Bridge.InsertMenu(TrayMenu, 0, Native.Constants.MF_BYPOSITION | Native.Constants.MF_STRING | (mode == AwakeMode.INDEFINITE ? Native.Constants.MF_CHECKED : Native.Constants.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_INDEFINITE, "Keep awake indefinitely");
|
||||
Bridge.InsertMenu(TrayMenu, 0, Native.Constants.MF_BYPOSITION | Native.Constants.MF_POPUP | (mode == AwakeMode.TIMED ? Native.Constants.MF_CHECKED : Native.Constants.MF_UNCHECKED), (uint)awakeTimeMenu, "Keep awake on interval");
|
||||
Bridge.InsertMenu(TrayMenu, 0, Native.Constants.MF_BYPOSITION | Native.Constants.MF_STRING | Native.Constants.MF_DISABLED | (mode == AwakeMode.EXPIRABLE ? Native.Constants.MF_CHECKED : Native.Constants.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_EXPIRABLE, "Keep awake until expiration date and time");
|
||||
|
||||
TrayIcon.Text = text;
|
||||
}
|
||||
|
||||
private sealed class CheckButtonToolStripMenuItemAccessibleObject : ToolStripItem.ToolStripItemAccessibleObject
|
||||
{
|
||||
private CheckButtonToolStripMenuItem _menuItem;
|
||||
private readonly CheckButtonToolStripMenuItem _menuItem;
|
||||
|
||||
public CheckButtonToolStripMenuItemAccessibleObject(CheckButtonToolStripMenuItem menuItem)
|
||||
: base(menuItem)
|
||||
@ -149,23 +155,13 @@ namespace Awake.Core
|
||||
_menuItem = menuItem;
|
||||
}
|
||||
|
||||
public override AccessibleRole Role
|
||||
{
|
||||
get
|
||||
{
|
||||
return AccessibleRole.CheckButton;
|
||||
}
|
||||
}
|
||||
public override AccessibleRole Role => AccessibleRole.CheckButton;
|
||||
|
||||
public override string Name => _menuItem.Text + ", " + Role + ", " + (_menuItem.Checked ? "Checked" : "Unchecked");
|
||||
}
|
||||
|
||||
private sealed class CheckButtonToolStripMenuItem : ToolStripMenuItem
|
||||
{
|
||||
public CheckButtonToolStripMenuItem()
|
||||
{
|
||||
}
|
||||
|
||||
protected override AccessibleObject CreateAccessibilityInstance()
|
||||
{
|
||||
return new CheckButtonToolStripMenuItemAccessibleObject(this);
|
||||
|
@ -10,9 +10,6 @@ using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using Awake.Core.Models;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Windows.Win32;
|
||||
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
|
||||
namespace Awake.Core
|
||||
{
|
||||
@ -20,7 +17,7 @@ namespace Awake.Core
|
||||
{
|
||||
private static SettingsUtils? _moduleSettings;
|
||||
|
||||
private static SettingsUtils ModuleSettings { get => _moduleSettings; set => _moduleSettings = value; }
|
||||
private static SettingsUtils? ModuleSettings { get => _moduleSettings; set => _moduleSettings = value; }
|
||||
|
||||
private static ManualResetEvent? _exitSignal;
|
||||
|
||||
@ -36,7 +33,7 @@ namespace Awake.Core
|
||||
|
||||
switch (m.Msg)
|
||||
{
|
||||
case (int)PInvoke.WM_COMMAND:
|
||||
case (int)Native.Constants.WM_COMMAND:
|
||||
var targetCommandIndex = m.WParam.ToInt64() & 0xFFFF;
|
||||
switch (targetCommandIndex)
|
||||
{
|
||||
@ -44,26 +41,26 @@ namespace Awake.Core
|
||||
ExitCommandHandler(_exitSignal);
|
||||
break;
|
||||
case (long)TrayCommands.TC_DISPLAY_SETTING:
|
||||
DisplaySettingCommandHandler(InternalConstants.AppName);
|
||||
DisplaySettingCommandHandler(Constants.AppName);
|
||||
break;
|
||||
case (long)TrayCommands.TC_MODE_INDEFINITE:
|
||||
IndefiniteKeepAwakeCommandHandler(InternalConstants.AppName);
|
||||
IndefiniteKeepAwakeCommandHandler(Constants.AppName);
|
||||
break;
|
||||
case (long)TrayCommands.TC_MODE_PASSIVE:
|
||||
PassiveKeepAwakeCommandHandler(InternalConstants.AppName);
|
||||
PassiveKeepAwakeCommandHandler(Constants.AppName);
|
||||
break;
|
||||
case var _ when targetCommandIndex >= trayCommandsSize:
|
||||
// Format for the timer block:
|
||||
// TrayCommands.TC_TIME + ZERO_BASED_INDEX_IN_SETTINGS
|
||||
AwakeSettings settings = ModuleSettings.GetSettings<AwakeSettings>(InternalConstants.AppName);
|
||||
AwakeSettings settings = ModuleSettings!.GetSettings<AwakeSettings>(Constants.AppName);
|
||||
if (settings.Properties.CustomTrayTimes.Count == 0)
|
||||
{
|
||||
settings.Properties.CustomTrayTimes.AddRange(APIHelper.GetDefaultTrayOptions());
|
||||
settings.Properties.CustomTrayTimes.AddRange(Manager.GetDefaultTrayOptions());
|
||||
}
|
||||
|
||||
int index = (int)targetCommandIndex - (int)TrayCommands.TC_TIME;
|
||||
var targetTime = settings.Properties.CustomTrayTimes.ElementAt(index).Value;
|
||||
TimedKeepAwakeCommandHandler(InternalConstants.AppName, targetTime);
|
||||
TimedKeepAwakeCommandHandler(Constants.AppName, targetTime);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -75,7 +72,7 @@ namespace Awake.Core
|
||||
|
||||
private static void ExitCommandHandler(ManualResetEvent? exitSignal)
|
||||
{
|
||||
APIHelper.CompleteExit(0, exitSignal, true);
|
||||
Manager.CompleteExit(0, exitSignal, true);
|
||||
}
|
||||
|
||||
private static void DisplaySettingCommandHandler(string moduleName)
|
||||
@ -84,7 +81,7 @@ namespace Awake.Core
|
||||
|
||||
try
|
||||
{
|
||||
currentSettings = ModuleSettings.GetSettings<AwakeSettings>(moduleName);
|
||||
currentSettings = ModuleSettings!.GetSettings<AwakeSettings>(moduleName);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
@ -93,7 +90,7 @@ namespace Awake.Core
|
||||
|
||||
currentSettings.Properties.KeepDisplayOn = !currentSettings.Properties.KeepDisplayOn;
|
||||
|
||||
ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
|
||||
ModuleSettings!.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
|
||||
}
|
||||
|
||||
private static void TimedKeepAwakeCommandHandler(string moduleName, int seconds)
|
||||
@ -104,7 +101,7 @@ namespace Awake.Core
|
||||
|
||||
try
|
||||
{
|
||||
currentSettings = ModuleSettings.GetSettings<AwakeSettings>(moduleName);
|
||||
currentSettings = ModuleSettings!.GetSettings<AwakeSettings>(moduleName);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
@ -115,7 +112,7 @@ namespace Awake.Core
|
||||
currentSettings.Properties.IntervalHours = (uint)timeSpan.Hours;
|
||||
currentSettings.Properties.IntervalMinutes = (uint)timeSpan.Minutes;
|
||||
|
||||
ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
|
||||
ModuleSettings!.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
|
||||
}
|
||||
|
||||
private static void PassiveKeepAwakeCommandHandler(string moduleName)
|
||||
@ -124,7 +121,7 @@ namespace Awake.Core
|
||||
|
||||
try
|
||||
{
|
||||
currentSettings = ModuleSettings.GetSettings<AwakeSettings>(moduleName);
|
||||
currentSettings = ModuleSettings!.GetSettings<AwakeSettings>(moduleName);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
@ -133,7 +130,7 @@ namespace Awake.Core
|
||||
|
||||
currentSettings.Properties.Mode = AwakeMode.PASSIVE;
|
||||
|
||||
ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
|
||||
ModuleSettings!.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
|
||||
}
|
||||
|
||||
private static void IndefiniteKeepAwakeCommandHandler(string moduleName)
|
||||
@ -142,7 +139,7 @@ namespace Awake.Core
|
||||
|
||||
try
|
||||
{
|
||||
currentSettings = ModuleSettings.GetSettings<AwakeSettings>(moduleName);
|
||||
currentSettings = ModuleSettings!.GetSettings<AwakeSettings>(moduleName);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
@ -151,7 +148,7 @@ namespace Awake.Core
|
||||
|
||||
currentSettings.Properties.Mode = AwakeMode.INDEFINITE;
|
||||
|
||||
ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
|
||||
ModuleSettings!.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<variable name="buildId" value="NOBLE_SIX_02162023" />
|
||||
|
||||
<targets async="true">
|
||||
<target name="logfile"
|
||||
xsi:type="File"
|
||||
fileName="${specialfolder:folder=LocalApplicationData}/Microsoft/PowerToys/Awake/Logs/${var:awakeversion}/applog_${date:format=yyyy-MM-dd_HH}_${var:buildId}.txt"
|
||||
layout="[${longdate} ${level:uppercase=true} ${logger}] ${message}"
|
||||
archiveEvery="Day"
|
||||
archiveNumbering="Rolling"
|
||||
maxArchiveFiles="30"/>
|
||||
<target name="logconsole"
|
||||
xsi:type="Console"
|
||||
layout="[${longdate} ${level:uppercase=true}] ${message}" />
|
||||
</targets>
|
||||
|
||||
<rules>
|
||||
<logger name="*" minlevel="Info" writeTo="logconsole" />
|
||||
<logger name="*" minlevel="Debug" writeTo="logfile" />
|
||||
</rules>
|
||||
</nlog>
|
@ -1,23 +0,0 @@
|
||||
AllocConsole
|
||||
CreateFile
|
||||
CreatePopupMenu
|
||||
DestroyMenu
|
||||
DestroyWindow
|
||||
FindWindowEx
|
||||
GetClassName
|
||||
GetCurrentThreadId
|
||||
GetPwrCapabilities
|
||||
GetWindowThreadProcessId
|
||||
HMENU
|
||||
InsertMenu
|
||||
PostQuitMessage
|
||||
SendMessage
|
||||
SetConsoleCtrlHandler
|
||||
SetForegroundWindow
|
||||
SetStdHandle
|
||||
SetThreadExecutionState
|
||||
TrackPopupMenuEx
|
||||
WM_CLOSE
|
||||
WM_COMMAND
|
||||
WM_GETTEXT
|
||||
WM_USER
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user