mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
Awake vNext - NOBLE_SIX_02162023 (#24183)
* Initial scaffolding for expiration configuration * Simplifying the code and adding support for expiration events * Bit more cleanup * Initial support for expirable keep-awake * Update some of the threading logic * Logging and timing consistency * Initial UI scaffolding * Fix pathing issue for the icon when using config file * Add missing definitions * Update with basic interface * Cleanup redundant calls * Update name per convention * Simplify declaration * Proper binding to secondary Time property * Cleanup the terminology use * Standardize naming conventions. * More Awake cleanup * Ability to update the UI when the tray icon updates * Small tweaks before ViewModel refactor * Refactor the view model logic * Some consistency fixes * Remove the build props change * Add settings scaffolding when a file does not exist * Update expect.txt * Fix typos * Update build in logs * Updating based on discussion in #24183. This specifically addresses the fact that the `ExpirationDateTime` property was incorrectly auto-initialized to `DateTime.MinValue` when it should've been set to `DateTimeOffset.MinValue` to be consistent with the underlying type and assumptions around date/time. --------- Co-authored-by: Clint Rutkas <clint@rutkas.com>
This commit is contained in:
1
.github/actions/spell-check/expect.txt
vendored
1
.github/actions/spell-check/expect.txt
vendored
@@ -227,6 +227,7 @@ clientside
|
|||||||
CLIPCHILDREN
|
CLIPCHILDREN
|
||||||
Clipperton
|
Clipperton
|
||||||
CLIPSIBLINGS
|
CLIPSIBLINGS
|
||||||
|
Cloneable
|
||||||
clrcall
|
clrcall
|
||||||
Cls
|
Cls
|
||||||
CLSCTX
|
CLSCTX
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Reactive.Concurrency;
|
||||||
|
using System.Reactive.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -82,31 +84,54 @@ namespace Awake.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetIndefiniteKeepAwake(Action<bool> callback, Action failureCallback, bool keepDisplayOn = false)
|
private static bool SetAwakeStateBasedOnDisplaySetting(bool keepDisplayOn)
|
||||||
{
|
{
|
||||||
PowerToysTelemetry.Log.WriteEvent(new Awake.Telemetry.AwakeIndefinitelyKeepAwakeEvent());
|
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();
|
_tokenSource.Cancel();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_log.Info("Attempting to ensure that the thread is properly cleaned up...");
|
||||||
|
|
||||||
if (_runnerThread != null && !_runnerThread.IsCanceled)
|
if (_runnerThread != null && !_runnerThread.IsCanceled)
|
||||||
{
|
{
|
||||||
_runnerThread.Wait(_threadToken);
|
_runnerThread.Wait(_threadToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_log.Info("Thread is clean.");
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
_log.Info("Confirmed background thread cancellation when setting indefinite keep awake.");
|
_log.Info("Confirmed background thread cancellation when disabling explicit keep awake.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_tokenSource = new CancellationTokenSource();
|
_tokenSource = new CancellationTokenSource();
|
||||||
_threadToken = _tokenSource.Token;
|
_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
|
try
|
||||||
{
|
{
|
||||||
_runnerThread = Task.Run(() => RunIndefiniteLoop(keepDisplayOn), _threadToken)
|
_runnerThread = Task.Run(() => RunIndefiniteJob(keepDisplayOn), _threadToken)
|
||||||
.ContinueWith((result) => callback(result.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
|
.ContinueWith((result) => callback, TaskContinuationOptions.OnlyOnRanToCompletion)
|
||||||
.ContinueWith((result) => failureCallback, TaskContinuationOptions.NotOnRanToCompletion);
|
.ContinueWith((result) => failureCallback, TaskContinuationOptions.NotOnRanToCompletion);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -117,80 +142,101 @@ namespace Awake.Core
|
|||||||
|
|
||||||
public static void SetNoKeepAwake()
|
public static void SetNoKeepAwake()
|
||||||
{
|
{
|
||||||
_tokenSource.Cancel();
|
PowerToysTelemetry.Log.WriteEvent(new Awake.Telemetry.AwakeNoKeepAwakeEvent());
|
||||||
|
|
||||||
try
|
CancelExistingThread();
|
||||||
{
|
|
||||||
if (_runnerThread != null && !_runnerThread.IsCanceled)
|
|
||||||
{
|
|
||||||
_runnerThread.Wait(_threadToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
_log.Info("Confirmed background thread cancellation when disabling explicit keep awake.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetTimedKeepAwake(uint seconds, Action<bool> callback, Action failureCallback, bool keepDisplayOn = true)
|
public static void SetExpirableKeepAwake(DateTimeOffset expireAt, Action callback, Action failureCallback, bool keepDisplayOn = true)
|
||||||
{
|
{
|
||||||
PowerToysTelemetry.Log.WriteEvent(new Awake.Telemetry.AwakeTimedKeepAwakeEvent());
|
PowerToysTelemetry.Log.WriteEvent(new Awake.Telemetry.AwakeExpirableKeepAwakeEvent());
|
||||||
|
|
||||||
_tokenSource.Cancel();
|
CancelExistingThread();
|
||||||
|
|
||||||
try
|
if (expireAt > DateTime.Now && expireAt != null)
|
||||||
{
|
{
|
||||||
if (_runnerThread != null && !_runnerThread.IsCanceled)
|
_runnerThread = Task.Run(() => RunExpiringJob(expireAt, keepDisplayOn), _threadToken)
|
||||||
{
|
.ContinueWith((result) => callback, TaskContinuationOptions.OnlyOnRanToCompletion)
|
||||||
_runnerThread.Wait(_threadToken);
|
.ContinueWith((result) => failureCallback, TaskContinuationOptions.NotOnRanToCompletion);
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
_log.Info("Confirmed background thread cancellation when setting timed keep awake.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_tokenSource = new CancellationTokenSource();
|
|
||||||
_threadToken = _tokenSource.Token;
|
|
||||||
|
|
||||||
_runnerThread = Task.Run(() => RunTimedLoop(seconds, keepDisplayOn), _threadToken)
|
|
||||||
.ContinueWith((result) => callback(result.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
|
|
||||||
.ContinueWith((result) => failureCallback, TaskContinuationOptions.NotOnRanToCompletion);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool RunIndefiniteLoop(bool keepDisplayOn = false)
|
|
||||||
{
|
|
||||||
bool success;
|
|
||||||
if (keepDisplayOn)
|
|
||||||
{
|
|
||||||
success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
|
// 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
|
try
|
||||||
{
|
{
|
||||||
|
success = SetAwakeStateBasedOnDisplaySetting(keepDisplayOn);
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
_log.Info($"Initiated indefinite keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
|
_log.Info($"Initiated expirable keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
|
||||||
|
|
||||||
WaitHandle.WaitAny(new[] { _threadToken.WaitHandle });
|
Observable.Timer(expireAt, Scheduler.CurrentThread).Subscribe(
|
||||||
|
_ =>
|
||||||
return success;
|
{
|
||||||
|
_log.Info($"Completed expirable thread in {PInvoke.GetCurrentThreadId()}.");
|
||||||
|
CancelExistingThread();
|
||||||
|
},
|
||||||
|
_tokenSource.Token);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_log.Info("Could not successfully set up indefinite keep awake.");
|
_log.Info("Could not successfully set up expirable keep awake.");
|
||||||
return success;
|
}
|
||||||
|
}
|
||||||
|
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)
|
catch (OperationCanceledException ex)
|
||||||
{
|
{
|
||||||
// Task was clearly cancelled.
|
// Task was clearly cancelled.
|
||||||
_log.Info($"Background thread termination: {PInvoke.GetCurrentThreadId()}. Message: {ex.Message}");
|
_log.Info($"Background thread termination: {PInvoke.GetCurrentThreadId()}. Message: {ex.Message}");
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,59 +267,38 @@ namespace Awake.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool RunTimedLoop(uint seconds, bool keepDisplayOn = true)
|
private static void RunTimedJob(uint seconds, bool keepDisplayOn = true)
|
||||||
{
|
{
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
// In case cancellation was already requested.
|
// In case cancellation was already requested.
|
||||||
_threadToken.ThrowIfCancellationRequested();
|
_threadToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (keepDisplayOn)
|
success = SetAwakeStateBasedOnDisplaySetting(keepDisplayOn);
|
||||||
{
|
|
||||||
success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
_log.Info($"Initiated temporary keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
|
_log.Info($"Initiated timed keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
|
||||||
|
|
||||||
_timedLoopTimer = new System.Timers.Timer((seconds * 1000) + 1);
|
Observable.Timer(TimeSpan.FromSeconds(seconds), Scheduler.CurrentThread).Subscribe(
|
||||||
_timedLoopTimer.Elapsed += (s, e) =>
|
_ =>
|
||||||
{
|
{
|
||||||
_tokenSource.Cancel();
|
_log.Info($"Completed timed thread in {PInvoke.GetCurrentThreadId()}.");
|
||||||
|
CancelExistingThread();
|
||||||
_timedLoopTimer.Stop();
|
},
|
||||||
};
|
_tokenSource.Token);
|
||||||
|
|
||||||
_timedLoopTimer.Disposed += (s, e) =>
|
|
||||||
{
|
|
||||||
_log.Info("Old timer disposed.");
|
|
||||||
};
|
|
||||||
|
|
||||||
_timedLoopTimer.Start();
|
|
||||||
|
|
||||||
WaitHandle.WaitAny(new[] { _threadToken.WaitHandle });
|
|
||||||
_timedLoopTimer.Stop();
|
|
||||||
_timedLoopTimer.Dispose();
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_log.Info("Could not set up timed keep-awake with display on.");
|
_log.Info("Could not set up timed keep-awake with display on.");
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException ex)
|
catch (OperationCanceledException ex)
|
||||||
{
|
{
|
||||||
// Task was clearly cancelled.
|
// Task was clearly cancelled.
|
||||||
_log.Info($"Background thread termination: {PInvoke.GetCurrentThreadId()}. Message: {ex.Message}");
|
_log.Info($"Background thread termination: {PInvoke.GetCurrentThreadId()}. Message: {ex.Message}");
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,10 +382,12 @@ namespace Awake.Core
|
|||||||
|
|
||||||
public static Dictionary<string, int> GetDefaultTrayOptions()
|
public static Dictionary<string, int> GetDefaultTrayOptions()
|
||||||
{
|
{
|
||||||
Dictionary<string, int> optionsList = new Dictionary<string, int>();
|
Dictionary<string, int> optionsList = new Dictionary<string, int>
|
||||||
optionsList.Add("30 minutes", 1800);
|
{
|
||||||
optionsList.Add("1 hour", 3600);
|
{ "30 minutes", 1800 },
|
||||||
optionsList.Add("2 hours", 7200);
|
{ "1 hour", 3600 },
|
||||||
|
{ "2 hours", 7200 },
|
||||||
|
};
|
||||||
return optionsList;
|
return optionsList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +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.
|
|
||||||
|
|
||||||
namespace Awake.Core.Models
|
|
||||||
{
|
|
||||||
public struct BatteryReportingScale
|
|
||||||
{
|
|
||||||
public uint Granularity;
|
|
||||||
public uint Capacity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +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.
|
|
||||||
|
|
||||||
namespace Awake.Core.Models
|
|
||||||
{
|
|
||||||
// See: https://learn.microsoft.com/windows/console/handlerroutine
|
|
||||||
public enum ControlType
|
|
||||||
{
|
|
||||||
CTRL_C_EVENT = 0,
|
|
||||||
CTRL_BREAK_EVENT = 1,
|
|
||||||
CTRL_CLOSE_EVENT = 2,
|
|
||||||
CTRL_LOGOFF_EVENT = 5,
|
|
||||||
CTRL_SHUTDOWN_EVENT = 6,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,7 +11,8 @@ namespace Awake.Core.Models
|
|||||||
TC_DISPLAY_SETTING = PInvoke.WM_USER + 1,
|
TC_DISPLAY_SETTING = PInvoke.WM_USER + 1,
|
||||||
TC_MODE_PASSIVE = PInvoke.WM_USER + 2,
|
TC_MODE_PASSIVE = PInvoke.WM_USER + 2,
|
||||||
TC_MODE_INDEFINITE = PInvoke.WM_USER + 3,
|
TC_MODE_INDEFINITE = PInvoke.WM_USER + 3,
|
||||||
TC_EXIT = PInvoke.WM_USER + 4,
|
TC_MODE_EXPIRABLE = PInvoke.WM_USER + 4,
|
||||||
TC_TIME = PInvoke.WM_USER + 5,
|
TC_EXIT = PInvoke.WM_USER + 100,
|
||||||
|
TC_TIME = PInvoke.WM_USER + 101,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,13 @@ using Windows.Win32.UI.WindowsAndMessaging;
|
|||||||
|
|
||||||
namespace Awake.Core
|
namespace Awake.Core
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helper class used to manage the system tray.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Because Awake is a console application, there is no built-in
|
||||||
|
/// way to embed UI components so we have to heavily rely on the native Windows API.
|
||||||
|
/// </remarks>
|
||||||
internal static class TrayHelper
|
internal static class TrayHelper
|
||||||
{
|
{
|
||||||
private static readonly Logger _log;
|
private static readonly Logger _log;
|
||||||
@@ -89,7 +96,7 @@ namespace Awake.Core
|
|||||||
text,
|
text,
|
||||||
settings.Properties.KeepDisplayOn,
|
settings.Properties.KeepDisplayOn,
|
||||||
settings.Properties.Mode,
|
settings.Properties.Mode,
|
||||||
settings.Properties.TrayTimeShortcuts,
|
settings.Properties.CustomTrayTimes,
|
||||||
startedFromPowerToys);
|
startedFromPowerToys);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,19 +123,18 @@ namespace Awake.Core
|
|||||||
trayTimeShortcuts.AddRange(APIHelper.GetDefaultTrayOptions());
|
trayTimeShortcuts.AddRange(APIHelper.GetDefaultTrayOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make sure that this loads from JSON instead of being hard-coded.
|
|
||||||
var awakeTimeMenu = new DestroyMenuSafeHandle(PInvoke.CreatePopupMenu(), false);
|
var awakeTimeMenu = new DestroyMenuSafeHandle(PInvoke.CreatePopupMenu(), false);
|
||||||
for (int i = 0; i < trayTimeShortcuts.Count; i++)
|
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);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
var modeMenu = new DestroyMenuSafeHandle(PInvoke.CreatePopupMenu(), false);
|
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_SEPARATOR, 0, string.Empty);
|
||||||
PInvoke.InsertMenu(modeMenu, 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(modeMenu, 1, 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(modeMenu, 2, 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 temporarily");
|
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_POPUP, (uint)modeMenu.DangerousGetHandle(), "Mode");
|
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");
|
||||||
|
|
||||||
TrayIcon.Text = text;
|
TrayIcon.Text = text;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,13 +56,13 @@ namespace Awake.Core
|
|||||||
// Format for the timer block:
|
// Format for the timer block:
|
||||||
// TrayCommands.TC_TIME + ZERO_BASED_INDEX_IN_SETTINGS
|
// TrayCommands.TC_TIME + ZERO_BASED_INDEX_IN_SETTINGS
|
||||||
AwakeSettings settings = ModuleSettings.GetSettings<AwakeSettings>(InternalConstants.AppName);
|
AwakeSettings settings = ModuleSettings.GetSettings<AwakeSettings>(InternalConstants.AppName);
|
||||||
if (settings.Properties.TrayTimeShortcuts.Count == 0)
|
if (settings.Properties.CustomTrayTimes.Count == 0)
|
||||||
{
|
{
|
||||||
settings.Properties.TrayTimeShortcuts.AddRange(APIHelper.GetDefaultTrayOptions());
|
settings.Properties.CustomTrayTimes.AddRange(APIHelper.GetDefaultTrayOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = (int)targetCommandIndex - (int)TrayCommands.TC_TIME;
|
int index = (int)targetCommandIndex - (int)TrayCommands.TC_TIME;
|
||||||
var targetTime = settings.Properties.TrayTimeShortcuts.ElementAt(index).Value;
|
var targetTime = settings.Properties.CustomTrayTimes.ElementAt(index).Value;
|
||||||
TimedKeepAwakeCommandHandler(InternalConstants.AppName, targetTime);
|
TimedKeepAwakeCommandHandler(InternalConstants.AppName, targetTime);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -112,8 +112,8 @@ namespace Awake.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
currentSettings.Properties.Mode = AwakeMode.TIMED;
|
currentSettings.Properties.Mode = AwakeMode.TIMED;
|
||||||
currentSettings.Properties.Hours = (uint)timeSpan.Hours;
|
currentSettings.Properties.IntervalHours = (uint)timeSpan.Hours;
|
||||||
currentSettings.Properties.Minutes = (uint)timeSpan.Minutes;
|
currentSettings.Properties.IntervalMinutes = (uint)timeSpan.Minutes;
|
||||||
|
|
||||||
ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
|
ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
|
||||||
<variable name="buildId" value="LIBRARIAN_03202022" />
|
<variable name="buildId" value="NOBLE_SIX_02162023" />
|
||||||
|
|
||||||
<targets async="true">
|
<targets async="true">
|
||||||
<target name="logfile"
|
<target name="logfile"
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace Awake
|
|||||||
// Format of the build ID is: CODENAME_MMDDYYYY, where MMDDYYYY
|
// Format of the build ID is: CODENAME_MMDDYYYY, where MMDDYYYY
|
||||||
// is representative of the date when the last change was made before
|
// is representative of the date when the last change was made before
|
||||||
// the pull request is issued.
|
// the pull request is issued.
|
||||||
private static readonly string BuildId = "ARBITER_01312022";
|
private static readonly string BuildId = "NOBLE_SIX_02162023";
|
||||||
|
|
||||||
private static Mutex? _mutex;
|
private static Mutex? _mutex;
|
||||||
private static FileSystemWatcher? _watcher;
|
private static FileSystemWatcher? _watcher;
|
||||||
@@ -65,7 +65,7 @@ namespace Awake
|
|||||||
|
|
||||||
if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredAwakeEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
|
if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredAwakeEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
|
||||||
{
|
{
|
||||||
Exit("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.", 1, _exitSignal, true);
|
Exit("PowerToys.Awake tried to start with a group policy setting that disables the tool. Please contact your system administrator.", 1, _exitSignal, true);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ namespace Awake
|
|||||||
|
|
||||||
_log.Info("Parsing parameters...");
|
_log.Info("Parsing parameters...");
|
||||||
|
|
||||||
var configOption = new Option<bool>(
|
Option<bool> configOption = new(
|
||||||
aliases: new[] { "--use-pt-config", "-c" },
|
aliases: new[] { "--use-pt-config", "-c" },
|
||||||
getDefaultValue: () => false,
|
getDefaultValue: () => false,
|
||||||
description: $"Specifies whether {InternalConstants.AppName} will be using the PowerToys configuration file for managing the state.")
|
description: $"Specifies whether {InternalConstants.AppName} will be using the PowerToys configuration file for managing the state.")
|
||||||
@@ -106,11 +106,10 @@ namespace Awake
|
|||||||
{
|
{
|
||||||
Arity = ArgumentArity.ZeroOrOne,
|
Arity = ArgumentArity.ZeroOrOne,
|
||||||
},
|
},
|
||||||
|
Required = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
configOption.Required = false;
|
Option<bool> displayOption = new(
|
||||||
|
|
||||||
var displayOption = new Option<bool>(
|
|
||||||
aliases: new[] { "--display-on", "-d" },
|
aliases: new[] { "--display-on", "-d" },
|
||||||
getDefaultValue: () => true,
|
getDefaultValue: () => true,
|
||||||
description: "Determines whether the display should be kept awake.")
|
description: "Determines whether the display should be kept awake.")
|
||||||
@@ -119,11 +118,10 @@ namespace Awake
|
|||||||
{
|
{
|
||||||
Arity = ArgumentArity.ZeroOrOne,
|
Arity = ArgumentArity.ZeroOrOne,
|
||||||
},
|
},
|
||||||
|
Required = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
displayOption.Required = false;
|
Option<uint> timeOption = new(
|
||||||
|
|
||||||
var timeOption = new Option<uint>(
|
|
||||||
aliases: new[] { "--time-limit", "-t" },
|
aliases: new[] { "--time-limit", "-t" },
|
||||||
getDefaultValue: () => 0,
|
getDefaultValue: () => 0,
|
||||||
description: "Determines the interval, in seconds, during which the computer is kept awake.")
|
description: "Determines the interval, in seconds, during which the computer is kept awake.")
|
||||||
@@ -132,34 +130,45 @@ namespace Awake
|
|||||||
{
|
{
|
||||||
Arity = ArgumentArity.ExactlyOne,
|
Arity = ArgumentArity.ExactlyOne,
|
||||||
},
|
},
|
||||||
|
Required = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
timeOption.Required = false;
|
Option<int> pidOption = new(
|
||||||
|
|
||||||
var pidOption = new Option<int>(
|
|
||||||
aliases: new[] { "--pid", "-p" },
|
aliases: new[] { "--pid", "-p" },
|
||||||
getDefaultValue: () => 0,
|
getDefaultValue: () => 0,
|
||||||
description: $"Bind the execution of {InternalConstants.AppName} to another process.")
|
description: $"Bind the execution of {InternalConstants.AppName} to another process. When the process ends, the system will resume managing the current sleep/display mode.")
|
||||||
{
|
{
|
||||||
Argument = new Argument<int>(() => 0)
|
Argument = new Argument<int>(() => 0)
|
||||||
{
|
{
|
||||||
Arity = ArgumentArity.ZeroOrOne,
|
Arity = ArgumentArity.ZeroOrOne,
|
||||||
},
|
},
|
||||||
|
Required = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
pidOption.Required = 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.")
|
||||||
|
{
|
||||||
|
Argument = new Argument<string>(() => string.Empty)
|
||||||
|
{
|
||||||
|
Arity = ArgumentArity.ZeroOrOne,
|
||||||
|
},
|
||||||
|
Required = false,
|
||||||
|
};
|
||||||
|
|
||||||
RootCommand? rootCommand = new RootCommand
|
RootCommand? rootCommand = new()
|
||||||
{
|
{
|
||||||
configOption,
|
configOption,
|
||||||
displayOption,
|
displayOption,
|
||||||
timeOption,
|
timeOption,
|
||||||
pidOption,
|
pidOption,
|
||||||
|
expireAtOption,
|
||||||
};
|
};
|
||||||
|
|
||||||
rootCommand.Description = InternalConstants.AppName;
|
rootCommand.Description = InternalConstants.AppName;
|
||||||
|
|
||||||
rootCommand.Handler = CommandHandler.Create<bool, bool, uint, int>(HandleCommandLineArguments);
|
rootCommand.Handler = CommandHandler.Create<bool, bool, uint, int, string>(HandleCommandLineArguments);
|
||||||
|
|
||||||
_log.Info("Parameter setup complete. Proceeding to the rest of the app initiation...");
|
_log.Info("Parameter setup complete. Proceeding to the rest of the app initiation...");
|
||||||
|
|
||||||
@@ -180,7 +189,7 @@ namespace Awake
|
|||||||
APIHelper.CompleteExit(exitCode, exitSignal, force);
|
APIHelper.CompleteExit(exitCode, exitSignal, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, uint timeLimit, int pid)
|
private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, uint timeLimit, int pid, string expireAt)
|
||||||
{
|
{
|
||||||
_handler += ExitHandler;
|
_handler += ExitHandler;
|
||||||
APIHelper.SetConsoleControlHandler(_handler, true);
|
APIHelper.SetConsoleControlHandler(_handler, true);
|
||||||
@@ -199,6 +208,7 @@ namespace Awake
|
|||||||
_log.Info($"The value for --display-on is: {displayOn}");
|
_log.Info($"The value for --display-on is: {displayOn}");
|
||||||
_log.Info($"The value for --time-limit is: {timeLimit}");
|
_log.Info($"The value for --time-limit is: {timeLimit}");
|
||||||
_log.Info($"The value for --pid is: {pid}");
|
_log.Info($"The value for --pid is: {pid}");
|
||||||
|
_log.Info($"The value for --expire is: {expireAt}");
|
||||||
|
|
||||||
if (usePtConfig)
|
if (usePtConfig)
|
||||||
{
|
{
|
||||||
@@ -214,41 +224,21 @@ namespace Awake
|
|||||||
Exit("Received a signal to end the process. Making sure we quit...", 0, _exitSignal, true);
|
Exit("Received a signal to end the process. Making sure we quit...", 0, _exitSignal, true);
|
||||||
}
|
}
|
||||||
}).Start();
|
}).Start();
|
||||||
TrayHelper.InitializeTray(InternalConstants.FullAppName, new Icon("modules/awake/images/awake.ico"), _exitSignal);
|
|
||||||
|
TrayHelper.InitializeTray(InternalConstants.FullAppName, new Icon(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "images/awake.ico")), _exitSignal);
|
||||||
|
|
||||||
string? settingsPath = _settingsUtils.GetSettingsFilePath(InternalConstants.AppName);
|
string? settingsPath = _settingsUtils.GetSettingsFilePath(InternalConstants.AppName);
|
||||||
_log.Info($"Reading configuration file: {settingsPath}");
|
_log.Info($"Reading configuration file: {settingsPath}");
|
||||||
|
|
||||||
_watcher = new FileSystemWatcher
|
if (!File.Exists(settingsPath))
|
||||||
{
|
{
|
||||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
string? errorString = $"The settings file does not exist. Scaffolding default configuration...";
|
||||||
Path = Path.GetDirectoryName(settingsPath),
|
|
||||||
#pragma warning restore CS8601 // Possible null reference assignment.
|
|
||||||
EnableRaisingEvents = true,
|
|
||||||
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.CreationTime,
|
|
||||||
Filter = Path.GetFileName(settingsPath),
|
|
||||||
};
|
|
||||||
|
|
||||||
IObservable<System.Reactive.EventPattern<FileSystemEventArgs>>? changedObservable = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
|
AwakeSettings scaffoldSettings = new AwakeSettings();
|
||||||
h => _watcher.Changed += h,
|
_settingsUtils.SaveSettings(JsonSerializer.Serialize(scaffoldSettings), InternalConstants.AppName);
|
||||||
h => _watcher.Changed -= h);
|
}
|
||||||
|
|
||||||
IObservable<System.Reactive.EventPattern<FileSystemEventArgs>>? createdObservable = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
|
ScaffoldConfiguration(settingsPath);
|
||||||
cre => _watcher.Created += cre,
|
|
||||||
cre => _watcher.Created -= cre);
|
|
||||||
|
|
||||||
IObservable<System.Reactive.EventPattern<FileSystemEventArgs>>? mergedObservable = Observable.Merge(changedObservable, createdObservable);
|
|
||||||
|
|
||||||
mergedObservable.Throttle(TimeSpan.FromMilliseconds(25))
|
|
||||||
.SubscribeOn(TaskPoolScheduler.Default)
|
|
||||||
.Select(e => e.EventArgs)
|
|
||||||
.Subscribe(HandleAwakeConfigChange);
|
|
||||||
|
|
||||||
TrayHelper.SetTray(InternalConstants.FullAppName, new AwakeSettings(), _startedFromPowerToys);
|
|
||||||
|
|
||||||
// Initially the file might not be updated, so we need to start processing
|
|
||||||
// settings right away.
|
|
||||||
ProcessSettings();
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -259,15 +249,43 @@ namespace Awake
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AwakeMode mode = timeLimit <= 0 ? AwakeMode.INDEFINITE : AwakeMode.TIMED;
|
// Date-based binding takes precedence over timed configuration, so we want to
|
||||||
|
// check for that first.
|
||||||
if (mode == AwakeMode.INDEFINITE)
|
if (!string.IsNullOrWhiteSpace(expireAt))
|
||||||
{
|
{
|
||||||
SetupIndefiniteKeepAwake(displayOn);
|
try
|
||||||
|
{
|
||||||
|
DateTime expirationDateTime = DateTime.Parse(expireAt);
|
||||||
|
if (expirationDateTime > DateTime.Now)
|
||||||
|
{
|
||||||
|
// We want to have a dedicated expirable keep-awake logic instead of
|
||||||
|
// 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.
|
||||||
|
SetupExpirableKeepAwake(expirationDateTime, displayOn);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_log.Info($"Target date is not in the future, therefore there is nothing to wait for.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_log.Error($"Could not parse date string {expireAt} into a viable date.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetupTimedKeepAwake(timeLimit, displayOn);
|
AwakeMode mode = timeLimit <= 0 ? AwakeMode.INDEFINITE : AwakeMode.TIMED;
|
||||||
|
|
||||||
|
if (mode == AwakeMode.INDEFINITE)
|
||||||
|
{
|
||||||
|
SetupIndefiniteKeepAwake(displayOn);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetupTimedKeepAwake(timeLimit, displayOn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,6 +301,45 @@ namespace Awake
|
|||||||
_exitSignal.WaitOne();
|
_exitSignal.WaitOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ScaffoldConfiguration(string settingsPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_watcher = new FileSystemWatcher
|
||||||
|
{
|
||||||
|
Path = Path.GetDirectoryName(settingsPath)!,
|
||||||
|
EnableRaisingEvents = true,
|
||||||
|
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.CreationTime,
|
||||||
|
Filter = Path.GetFileName(settingsPath),
|
||||||
|
};
|
||||||
|
|
||||||
|
IObservable<System.Reactive.EventPattern<FileSystemEventArgs>>? changedObservable = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
|
||||||
|
h => _watcher.Changed += h,
|
||||||
|
h => _watcher.Changed -= h);
|
||||||
|
|
||||||
|
IObservable<System.Reactive.EventPattern<FileSystemEventArgs>>? createdObservable = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
|
||||||
|
cre => _watcher.Created += cre,
|
||||||
|
cre => _watcher.Created -= cre);
|
||||||
|
|
||||||
|
IObservable<System.Reactive.EventPattern<FileSystemEventArgs>>? mergedObservable = Observable.Merge(changedObservable, createdObservable);
|
||||||
|
|
||||||
|
mergedObservable.Throttle(TimeSpan.FromMilliseconds(25))
|
||||||
|
.SubscribeOn(TaskPoolScheduler.Default)
|
||||||
|
.Select(e => e.EventArgs)
|
||||||
|
.Subscribe(HandleAwakeConfigChange);
|
||||||
|
|
||||||
|
TrayHelper.SetTray(InternalConstants.FullAppName, new AwakeSettings(), _startedFromPowerToys);
|
||||||
|
|
||||||
|
// Initially the file might not be updated, so we need to start processing
|
||||||
|
// settings right away.
|
||||||
|
ProcessSettings();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Error($"An error occurred scaffolding the configuration. Error details: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void SetupIndefiniteKeepAwake(bool displayOn)
|
private static void SetupIndefiniteKeepAwake(bool displayOn)
|
||||||
{
|
{
|
||||||
APIHelper.SetIndefiniteKeepAwake(LogCompletedKeepAwakeThread, LogUnexpectedOrCancelledKeepAwakeThreadCompletion, displayOn);
|
APIHelper.SetIndefiniteKeepAwake(LogCompletedKeepAwakeThread, LogUnexpectedOrCancelledKeepAwakeThreadCompletion, displayOn);
|
||||||
@@ -303,7 +360,7 @@ namespace Awake
|
|||||||
|
|
||||||
if (settings != null)
|
if (settings != null)
|
||||||
{
|
{
|
||||||
_log.Info($"Identified custom time shortcuts for the tray: {settings.Properties.TrayTimeShortcuts.Count}");
|
_log.Info($"Identified custom time shortcuts for the tray: {settings.Properties.CustomTrayTimes.Count}");
|
||||||
|
|
||||||
switch (settings.Properties.Mode)
|
switch (settings.Properties.Mode)
|
||||||
{
|
{
|
||||||
@@ -321,12 +378,19 @@ namespace Awake
|
|||||||
|
|
||||||
case AwakeMode.TIMED:
|
case AwakeMode.TIMED:
|
||||||
{
|
{
|
||||||
uint computedTime = (settings.Properties.Hours * 60 * 60) + (settings.Properties.Minutes * 60);
|
uint computedTime = (settings.Properties.IntervalHours * 60 * 60) + (settings.Properties.IntervalMinutes * 60);
|
||||||
SetupTimedKeepAwake(computedTime, settings.Properties.KeepDisplayOn);
|
SetupTimedKeepAwake(computedTime, settings.Properties.KeepDisplayOn);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case AwakeMode.EXPIRABLE:
|
||||||
|
{
|
||||||
|
SetupExpirableKeepAwake(settings.Properties.ExpirationDateTime, settings.Properties.KeepDisplayOn);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
string? errorMessage = "Unknown mode of operation. Check config file.";
|
string? errorMessage = "Unknown mode of operation. Check config file.";
|
||||||
@@ -360,6 +424,13 @@ namespace Awake
|
|||||||
APIHelper.SetNoKeepAwake();
|
APIHelper.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}.");
|
||||||
|
|
||||||
|
APIHelper.SetExpirableKeepAwake(expireAt, LogCompletedKeepAwakeThread, LogUnexpectedOrCancelledKeepAwakeThreadCompletion, displayOn);
|
||||||
|
}
|
||||||
|
|
||||||
private static void SetupTimedKeepAwake(uint time, bool 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}.");
|
_log.Info($"Timed keep-awake. Expected runtime: {time} seconds with display on setting set to {displayOn}.");
|
||||||
@@ -369,14 +440,14 @@ namespace Awake
|
|||||||
|
|
||||||
private static void LogUnexpectedOrCancelledKeepAwakeThreadCompletion()
|
private static void LogUnexpectedOrCancelledKeepAwakeThreadCompletion()
|
||||||
{
|
{
|
||||||
string? errorMessage = "The keep-awake thread was terminated early.";
|
string? errorMessage = "The keep awake thread was terminated early.";
|
||||||
_log.Info(errorMessage);
|
_log.Info(errorMessage);
|
||||||
_log.Debug(errorMessage);
|
_log.Debug(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void LogCompletedKeepAwakeThread(bool result)
|
private static void LogCompletedKeepAwakeThread()
|
||||||
{
|
{
|
||||||
_log.Info($"Exited keep-awake thread successfully: {result}");
|
_log.Info($"Exited keep awake thread successfully.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// 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.Diagnostics.Tracing;
|
||||||
|
using Microsoft.PowerToys.Telemetry;
|
||||||
|
using Microsoft.PowerToys.Telemetry.Events;
|
||||||
|
|
||||||
|
namespace Awake.Telemetry
|
||||||
|
{
|
||||||
|
[EventData]
|
||||||
|
public class AwakeExpirableKeepAwakeEvent : EventBase, IEvent
|
||||||
|
{
|
||||||
|
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/modules/awake/Awake/Telemetry/AwakeNoKeepAwakeEvent.cs
Normal file
16
src/modules/awake/Awake/Telemetry/AwakeNoKeepAwakeEvent.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// 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.Diagnostics.Tracing;
|
||||||
|
using Microsoft.PowerToys.Telemetry;
|
||||||
|
using Microsoft.PowerToys.Telemetry.Events;
|
||||||
|
|
||||||
|
namespace Awake.Telemetry
|
||||||
|
{
|
||||||
|
[EventData]
|
||||||
|
internal class AwakeNoKeepAwakeEvent : EventBase, IEvent
|
||||||
|
{
|
||||||
|
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
@@ -13,25 +14,29 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
|||||||
{
|
{
|
||||||
KeepDisplayOn = false;
|
KeepDisplayOn = false;
|
||||||
Mode = AwakeMode.PASSIVE;
|
Mode = AwakeMode.PASSIVE;
|
||||||
Hours = 0;
|
IntervalHours = 0;
|
||||||
Minutes = 0;
|
IntervalMinutes = 0;
|
||||||
TrayTimeShortcuts = new Dictionary<string, int>();
|
ExpirationDateTime = DateTimeOffset.MinValue;
|
||||||
|
CustomTrayTimes = new Dictionary<string, int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonPropertyName("awake_keep_display_on")]
|
[JsonPropertyName("keepDisplayOn")]
|
||||||
public bool KeepDisplayOn { get; set; }
|
public bool KeepDisplayOn { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("awake_mode")]
|
[JsonPropertyName("mode")]
|
||||||
public AwakeMode Mode { get; set; }
|
public AwakeMode Mode { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("awake_hours")]
|
[JsonPropertyName("intervalHours")]
|
||||||
public uint Hours { get; set; }
|
public uint IntervalHours { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("awake_minutes")]
|
[JsonPropertyName("intervalMinutes")]
|
||||||
public uint Minutes { get; set; }
|
public uint IntervalMinutes { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("tray_times")]
|
[JsonPropertyName("expirationDateTime")]
|
||||||
public Dictionary<string, int> TrayTimeShortcuts { get; set; }
|
public DateTimeOffset ExpirationDateTime { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("customTrayTimes")]
|
||||||
|
public Dictionary<string, int> CustomTrayTimes { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum AwakeMode
|
public enum AwakeMode
|
||||||
@@ -39,5 +44,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
|||||||
PASSIVE = 0,
|
PASSIVE = 0,
|
||||||
INDEFINITE = 1,
|
INDEFINITE = 1,
|
||||||
TIMED = 2,
|
TIMED = 2,
|
||||||
|
EXPIRABLE = 3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,17 @@
|
|||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||||
|
|
||||||
namespace Microsoft.PowerToys.Settings.UI.Library
|
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||||
{
|
{
|
||||||
public class AwakeSettings : BasePTModuleSettings, ISettingsConfig
|
public class AwakeSettings : BasePTModuleSettings, ISettingsConfig, ICloneable
|
||||||
{
|
{
|
||||||
public const string ModuleName = "Awake";
|
public const string ModuleName = "Awake";
|
||||||
public const string ModuleVersion = "0.0.1";
|
public const string ModuleVersion = "0.0.2";
|
||||||
|
|
||||||
public AwakeSettings()
|
public AwakeSettings()
|
||||||
{
|
{
|
||||||
@@ -22,6 +24,24 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
|||||||
[JsonPropertyName("properties")]
|
[JsonPropertyName("properties")]
|
||||||
public AwakeProperties Properties { get; set; }
|
public AwakeProperties Properties { get; set; }
|
||||||
|
|
||||||
|
public object Clone()
|
||||||
|
{
|
||||||
|
return new AwakeSettings()
|
||||||
|
{
|
||||||
|
Name = Name,
|
||||||
|
Version = Version,
|
||||||
|
Properties = new AwakeProperties()
|
||||||
|
{
|
||||||
|
CustomTrayTimes = Properties.CustomTrayTimes.ToDictionary(entry => entry.Key, entry => entry.Value),
|
||||||
|
Mode = Properties.Mode,
|
||||||
|
KeepDisplayOn = Properties.KeepDisplayOn,
|
||||||
|
IntervalMinutes = Properties.IntervalMinutes,
|
||||||
|
IntervalHours = Properties.IntervalHours,
|
||||||
|
ExpirationDateTime = Properties.ExpirationDateTime,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public string GetModuleName()
|
public string GetModuleName()
|
||||||
{
|
{
|
||||||
return Name;
|
return Name;
|
||||||
|
|||||||
@@ -7,5 +7,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Interfaces
|
|||||||
public interface ISettingsRepository<T>
|
public interface ISettingsRepository<T>
|
||||||
{
|
{
|
||||||
T SettingsConfig { get; set; }
|
T SettingsConfig { get; set; }
|
||||||
|
|
||||||
|
bool ReloadSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,23 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ReloadSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
T settingsItem = new T();
|
||||||
|
settingsConfig = _settingsUtils.GetSettingsOrDefault<T>(settingsItem.GetModuleName());
|
||||||
|
|
||||||
|
SettingsConfig = settingsConfig;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Settings configurations shared across all viewmodels
|
// Settings configurations shared across all viewmodels
|
||||||
public T SettingsConfig
|
public T SettingsConfig
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -48,6 +48,23 @@ namespace Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ReloadSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
T settingsItem = new T();
|
||||||
|
_settingsConfig = _settingsUtils.GetSettingsOrDefault<T>(settingsItem.GetModuleName());
|
||||||
|
|
||||||
|
SettingsConfig = _settingsConfig;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Mock<IFile> GetModuleIOProvider(string version, string module, string fileName)
|
public static Mock<IFile> GetModuleIOProvider(string version, string module, string fileName)
|
||||||
|
|||||||
@@ -1812,35 +1812,41 @@ From there, simply click on one of the supported files in the File Explorer and
|
|||||||
<data name="Awake.ModuleDescription" xml:space="preserve">
|
<data name="Awake.ModuleDescription" xml:space="preserve">
|
||||||
<value>A convenient way to keep your PC awake on-demand.</value>
|
<value>A convenient way to keep your PC awake on-demand.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Awake_EnableAwake.Header" xml:space="preserve">
|
<data name="Awake_EnableSettingsCard.Header" xml:space="preserve">
|
||||||
<value>Enable Awake</value>
|
<value>Enable Awake</value>
|
||||||
<comment>Awake is a product name, do not loc</comment>
|
<comment>Awake is a product name, do not loc</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="Awake_NoKeepAwake.Content" xml:space="preserve">
|
<data name="Awake_NoKeepAwakeSelector.Content" xml:space="preserve">
|
||||||
<value>Keep using the selected power plan</value>
|
<value>Keep using the selected power plan</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Awake_IndefiniteKeepAwake.Content" xml:space="preserve">
|
<data name="Awake_IndefiniteKeepAwakeSelector.Content" xml:space="preserve">
|
||||||
<value>Keep awake indefinitely</value>
|
<value>Keep awake indefinitely</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Awake_TemporaryKeepAwake.Content" xml:space="preserve">
|
<data name="Awake_TemporaryKeepAwakeSelector.Content" xml:space="preserve">
|
||||||
<value>Keep awake temporarily</value>
|
<value>Keep awake for a time interval</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Awake_EnableDisplayKeepAwake.Header" xml:space="preserve">
|
<data name="Awake_ExpirableKeepAwakeSelector.Content" xml:space="preserve">
|
||||||
|
<value>Keep awake until expiration</value>
|
||||||
|
</data>
|
||||||
|
<data name="Awake_DisplaySettingsCard.Header" xml:space="preserve">
|
||||||
<value>Keep screen on</value>
|
<value>Keep screen on</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Awake_EnableDisplayKeepAwake.Description" xml:space="preserve">
|
<data name="Awake_DisplaySettingsCard.Description" xml:space="preserve">
|
||||||
<value>This setting is only available when keeping the PC awake</value>
|
<value>This setting is only available when keeping the PC awake</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Awake_Mode.Header" xml:space="preserve">
|
<data name="Awake_ExpirationSettingsCard.Description" xml:space="preserve">
|
||||||
|
<value>Keep custom awakeness state until a specific date and time</value>
|
||||||
|
</data>
|
||||||
|
<data name="Awake_ModeSettingsCard.Header" xml:space="preserve">
|
||||||
<value>Mode</value>
|
<value>Mode</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Awake_Behavior_GroupSettings.Header" xml:space="preserve">
|
<data name="Awake_BehaviorSettingsGroup.Header" xml:space="preserve">
|
||||||
<value>Behavior</value>
|
<value>Behavior</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Awake_TemporaryKeepAwake_Hours.Header" xml:space="preserve">
|
<data name="Awake_IntervalHoursInput.Header" xml:space="preserve">
|
||||||
<value>Hours</value>
|
<value>Hours</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Awake_TemporaryKeepAwake_Minutes.Header" xml:space="preserve">
|
<data name="Awake_IntervalMinutesInput.Header" xml:space="preserve">
|
||||||
<value>Minutes</value>
|
<value>Minutes</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Oobe_Awake.Title" xml:space="preserve">
|
<data name="Oobe_Awake.Title" xml:space="preserve">
|
||||||
@@ -1930,7 +1936,7 @@ From there, simply click on one of the supported files in the File Explorer and
|
|||||||
<data name="SeeWhatsNew.Content" xml:space="preserve">
|
<data name="SeeWhatsNew.Content" xml:space="preserve">
|
||||||
<value>See what's new</value>
|
<value>See what's new</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Awake_Mode.Description" xml:space="preserve">
|
<data name="Awake_ModeSettingsCard.Description" xml:space="preserve">
|
||||||
<value>Manage the state of your device when Awake is active</value>
|
<value>Manage the state of your device when Awake is active</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExcludedApps.Header" xml:space="preserve">
|
<data name="ExcludedApps.Header" xml:space="preserve">
|
||||||
@@ -2120,8 +2126,11 @@ From there, simply click on one of the supported files in the File Explorer and
|
|||||||
<value>New size</value>
|
<value>New size</value>
|
||||||
<comment>First part of the default name of new sizes that can be added in PT's settings ui.</comment>
|
<comment>First part of the default name of new sizes that can be added in PT's settings ui.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="Awake_TimeBeforeAwake.Header" xml:space="preserve">
|
<data name="Awake_IntervalSettingsCard.Header" xml:space="preserve">
|
||||||
<value>Time before returning to the previous awakeness state</value>
|
<value>Interval before returning to the previous awakeness state</value>
|
||||||
|
</data>
|
||||||
|
<data name="Awake_ExpirationSettingsCard.Header" xml:space="preserve">
|
||||||
|
<value>End date and time</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MouseUtils.ModuleTitle" xml:space="preserve">
|
<data name="MouseUtils.ModuleTitle" xml:space="preserve">
|
||||||
<value>Mouse utilities</value>
|
<value>Mouse utilities</value>
|
||||||
@@ -3056,4 +3065,4 @@ Activate by holding the key for the character you want to add an accent to, then
|
|||||||
<data name="Hosts_Toggle_LoopbackDuplicates.Header" xml:space="preserve">
|
<data name="Hosts_Toggle_LoopbackDuplicates.Header" xml:space="preserve">
|
||||||
<value>Consider loopback addresses as duplicates</value>
|
<value>Consider loopback addresses as duplicates</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -4,62 +4,29 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using global::PowerToys.GPOWrapper;
|
|
||||||
using Microsoft.PowerToys.Settings.UI.Library;
|
using Microsoft.PowerToys.Settings.UI.Library;
|
||||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||||
|
|
||||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||||
{
|
{
|
||||||
public class AwakeViewModel : Observable
|
public class AwakeViewModel : Observable
|
||||||
{
|
{
|
||||||
private GeneralSettings GeneralSettingsConfig { get; set; }
|
public AwakeViewModel()
|
||||||
|
|
||||||
private AwakeSettings Settings { get; set; }
|
|
||||||
|
|
||||||
private Func<string, int> SendConfigMSG { get; }
|
|
||||||
|
|
||||||
public AwakeViewModel(ISettingsRepository<GeneralSettings> settingsRepository, ISettingsRepository<AwakeSettings> moduleSettingsRepository, Func<string, int> ipcMSGCallBackFunc)
|
|
||||||
{
|
{
|
||||||
// To obtain the general settings configurations of PowerToys Settings.
|
|
||||||
if (settingsRepository == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(settingsRepository));
|
|
||||||
}
|
|
||||||
|
|
||||||
GeneralSettingsConfig = settingsRepository.SettingsConfig;
|
|
||||||
|
|
||||||
// To obtain the settings configurations of Fancy zones.
|
|
||||||
if (moduleSettingsRepository == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(moduleSettingsRepository));
|
|
||||||
}
|
|
||||||
|
|
||||||
Settings = moduleSettingsRepository.SettingsConfig;
|
|
||||||
|
|
||||||
InitializeEnabledValue();
|
|
||||||
|
|
||||||
_keepDisplayOn = Settings.Properties.KeepDisplayOn;
|
|
||||||
_mode = Settings.Properties.Mode;
|
|
||||||
_hours = Settings.Properties.Hours;
|
|
||||||
_minutes = Settings.Properties.Minutes;
|
|
||||||
|
|
||||||
// set the callback functions value to hangle outgoing IPC message.
|
|
||||||
SendConfigMSG = ipcMSGCallBackFunc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeEnabledValue()
|
public AwakeSettings ModuleSettings
|
||||||
{
|
{
|
||||||
_enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredAwakeEnabledValue();
|
get => _moduleSettings;
|
||||||
if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
|
set
|
||||||
{
|
{
|
||||||
// Get the enabled state from GPO.
|
if (_moduleSettings != value)
|
||||||
_enabledStateIsGPOConfigured = true;
|
{
|
||||||
_isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
|
_moduleSettings = value;
|
||||||
}
|
RefreshModuleSettings();
|
||||||
else
|
RefreshEnabledState();
|
||||||
{
|
}
|
||||||
_isEnabled = GeneralSettingsConfig.Enabled.Awake;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,13 +45,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
|
|
||||||
_isEnabled = value;
|
_isEnabled = value;
|
||||||
|
|
||||||
GeneralSettingsConfig.Enabled.Awake = value;
|
RefreshEnabledState();
|
||||||
OnPropertyChanged(nameof(IsEnabled));
|
|
||||||
OnPropertyChanged(nameof(IsTimeConfigurationEnabled));
|
|
||||||
OnPropertyChanged(nameof(IsScreenConfigurationPossibleEnabled));
|
|
||||||
|
|
||||||
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
|
|
||||||
SendConfigMSG(outgoing.ToString());
|
|
||||||
NotifyPropertyChanged();
|
NotifyPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,31 +55,44 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
public bool IsEnabledGpoConfigured
|
public bool IsEnabledGpoConfigured
|
||||||
{
|
{
|
||||||
get => _enabledStateIsGPOConfigured;
|
get => _enabledStateIsGPOConfigured;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_enabledStateIsGPOConfigured != value)
|
||||||
|
{
|
||||||
|
_enabledStateIsGPOConfigured = value;
|
||||||
|
NotifyPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsExpirationConfigurationEnabled
|
||||||
|
{
|
||||||
|
get => ModuleSettings.Properties.Mode == AwakeMode.EXPIRABLE && IsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsTimeConfigurationEnabled
|
public bool IsTimeConfigurationEnabled
|
||||||
{
|
{
|
||||||
get => _mode == AwakeMode.TIMED && _isEnabled;
|
get => ModuleSettings.Properties.Mode == AwakeMode.TIMED && IsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsScreenConfigurationPossibleEnabled
|
public bool IsScreenConfigurationPossibleEnabled
|
||||||
{
|
{
|
||||||
get => _mode != AwakeMode.PASSIVE && _isEnabled;
|
get => ModuleSettings.Properties.Mode != AwakeMode.PASSIVE && IsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AwakeMode Mode
|
public AwakeMode Mode
|
||||||
{
|
{
|
||||||
get => _mode;
|
get => ModuleSettings.Properties.Mode;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_mode != value)
|
if (ModuleSettings.Properties.Mode != value)
|
||||||
{
|
{
|
||||||
_mode = value;
|
ModuleSettings.Properties.Mode = value;
|
||||||
OnPropertyChanged(nameof(Mode));
|
|
||||||
OnPropertyChanged(nameof(IsTimeConfigurationEnabled));
|
OnPropertyChanged(nameof(IsTimeConfigurationEnabled));
|
||||||
OnPropertyChanged(nameof(IsScreenConfigurationPossibleEnabled));
|
OnPropertyChanged(nameof(IsScreenConfigurationPossibleEnabled));
|
||||||
|
OnPropertyChanged(nameof(IsExpirationConfigurationEnabled));
|
||||||
|
|
||||||
Settings.Properties.Mode = value;
|
|
||||||
NotifyPropertyChanged();
|
NotifyPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,79 +100,93 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
|
|
||||||
public bool KeepDisplayOn
|
public bool KeepDisplayOn
|
||||||
{
|
{
|
||||||
get => _keepDisplayOn;
|
get => ModuleSettings.Properties.KeepDisplayOn;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_keepDisplayOn != value)
|
if (ModuleSettings.Properties.KeepDisplayOn != value)
|
||||||
{
|
{
|
||||||
_keepDisplayOn = value;
|
ModuleSettings.Properties.KeepDisplayOn = value;
|
||||||
OnPropertyChanged(nameof(KeepDisplayOn));
|
|
||||||
|
|
||||||
Settings.Properties.KeepDisplayOn = value;
|
|
||||||
NotifyPropertyChanged();
|
NotifyPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Hours
|
public uint IntervalHours
|
||||||
{
|
{
|
||||||
get => _hours;
|
get => ModuleSettings.Properties.IntervalHours;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_hours != value)
|
if (ModuleSettings.Properties.IntervalHours != value)
|
||||||
{
|
{
|
||||||
_hours = value;
|
ModuleSettings.Properties.IntervalHours = value;
|
||||||
OnPropertyChanged(nameof(Hours));
|
|
||||||
|
|
||||||
Settings.Properties.Hours = value;
|
|
||||||
NotifyPropertyChanged();
|
NotifyPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Minutes
|
public uint IntervalMinutes
|
||||||
{
|
{
|
||||||
get => _minutes;
|
get => ModuleSettings.Properties.IntervalMinutes;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_minutes != value)
|
if (ModuleSettings.Properties.IntervalMinutes != value)
|
||||||
{
|
{
|
||||||
_minutes = value;
|
ModuleSettings.Properties.IntervalMinutes = value;
|
||||||
OnPropertyChanged(nameof(Minutes));
|
|
||||||
|
|
||||||
Settings.Properties.Minutes = value;
|
|
||||||
NotifyPropertyChanged();
|
NotifyPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DateTimeOffset ExpirationDateTime
|
||||||
|
{
|
||||||
|
get => ModuleSettings.Properties.ExpirationDateTime;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (ModuleSettings.Properties.ExpirationDateTime != value)
|
||||||
|
{
|
||||||
|
ModuleSettings.Properties.ExpirationDateTime = value;
|
||||||
|
NotifyPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimeSpan ExpirationTime
|
||||||
|
{
|
||||||
|
get => ExpirationDateTime.TimeOfDay;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (ExpirationDateTime.TimeOfDay != value)
|
||||||
|
{
|
||||||
|
ExpirationDateTime = new DateTime(ExpirationDateTime.Year, ExpirationDateTime.Month, ExpirationDateTime.Day, value.Hours, value.Minutes, value.Seconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
|
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
|
||||||
{
|
{
|
||||||
|
Logger.LogInfo($"Changed the property {propertyName}");
|
||||||
OnPropertyChanged(propertyName);
|
OnPropertyChanged(propertyName);
|
||||||
if (SendConfigMSG != null)
|
|
||||||
{
|
|
||||||
SndAwakeSettings outsettings = new SndAwakeSettings(Settings);
|
|
||||||
SndModuleSettings<SndAwakeSettings> ipcMessage = new SndModuleSettings<SndAwakeSettings>(outsettings);
|
|
||||||
|
|
||||||
string targetMessage = ipcMessage.ToJsonString();
|
|
||||||
SendConfigMSG(targetMessage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RefreshEnabledState()
|
public void RefreshEnabledState()
|
||||||
{
|
{
|
||||||
InitializeEnabledValue();
|
|
||||||
OnPropertyChanged(nameof(IsEnabled));
|
OnPropertyChanged(nameof(IsEnabled));
|
||||||
OnPropertyChanged(nameof(IsTimeConfigurationEnabled));
|
OnPropertyChanged(nameof(IsTimeConfigurationEnabled));
|
||||||
OnPropertyChanged(nameof(IsScreenConfigurationPossibleEnabled));
|
OnPropertyChanged(nameof(IsScreenConfigurationPossibleEnabled));
|
||||||
|
OnPropertyChanged(nameof(IsExpirationConfigurationEnabled));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshModuleSettings()
|
||||||
|
{
|
||||||
|
OnPropertyChanged(nameof(Mode));
|
||||||
|
OnPropertyChanged(nameof(KeepDisplayOn));
|
||||||
|
OnPropertyChanged(nameof(IntervalHours));
|
||||||
|
OnPropertyChanged(nameof(IntervalMinutes));
|
||||||
|
OnPropertyChanged(nameof(ExpirationDateTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
private GpoRuleConfigured _enabledGpoRuleConfiguration;
|
|
||||||
private bool _enabledStateIsGPOConfigured;
|
private bool _enabledStateIsGPOConfigured;
|
||||||
|
private AwakeSettings _moduleSettings;
|
||||||
private bool _isEnabled;
|
private bool _isEnabled;
|
||||||
private uint _hours;
|
|
||||||
private uint _minutes;
|
|
||||||
private bool _keepDisplayOn;
|
|
||||||
private AwakeMode _mode;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
xmlns:labs="using:CommunityToolkit.Labs.WinUI"
|
xmlns:labs="using:CommunityToolkit.Labs.WinUI"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:ui="using:CommunityToolkit.WinUI.UI"
|
xmlns:ui="using:CommunityToolkit.WinUI.UI"
|
||||||
|
xmlns:viewmodels="using:Microsoft.PowerToys.Settings.UI.ViewModels"
|
||||||
|
d:DataContext="{d:DesignInstance Type=viewmodels:AwakeViewModel}"
|
||||||
AutomationProperties.LandmarkType="Main"
|
AutomationProperties.LandmarkType="Main"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
@@ -22,7 +24,7 @@
|
|||||||
<controls:SettingsPageControl.ModuleContent>
|
<controls:SettingsPageControl.ModuleContent>
|
||||||
<StackPanel Orientation="Vertical" ChildrenTransitions="{StaticResource SettingsCardsAnimations}">
|
<StackPanel Orientation="Vertical" ChildrenTransitions="{StaticResource SettingsCardsAnimations}">
|
||||||
<labs:SettingsCard
|
<labs:SettingsCard
|
||||||
x:Uid="Awake_EnableAwake"
|
x:Uid="Awake_EnableSettingsCard"
|
||||||
HeaderIcon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsAwake.png}"
|
HeaderIcon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsAwake.png}"
|
||||||
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabledGpoConfigured, Converter={StaticResource BoolNegationConverter}}">
|
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabledGpoConfigured, Converter={StaticResource BoolNegationConverter}}">
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
@@ -37,37 +39,48 @@
|
|||||||
Severity="Informational" />
|
Severity="Informational" />
|
||||||
|
|
||||||
<controls:SettingsGroup
|
<controls:SettingsGroup
|
||||||
x:Uid="Awake_Behavior_GroupSettings"
|
x:Uid="Awake_BehaviorSettingsGroup"
|
||||||
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}">
|
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}">
|
||||||
|
|
||||||
<labs:SettingsCard
|
<labs:SettingsCard
|
||||||
x:Uid="Awake_Mode"
|
x:Uid="Awake_ModeSettingsCard"
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily}, Glyph=}">
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily}, Glyph=}">
|
||||||
<ComboBox
|
<ComboBox
|
||||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||||
SelectedIndex="{x:Bind Path=ViewModel.Mode, Mode=TwoWay, Converter={StaticResource AwakeModeToIntConverter}}">
|
SelectedIndex="{x:Bind Path=ViewModel.Mode, Mode=TwoWay, Converter={StaticResource AwakeModeToIntConverter}}">
|
||||||
<ComboBoxItem x:Uid="Awake_NoKeepAwake" />
|
<ComboBoxItem x:Uid="Awake_NoKeepAwakeSelector" />
|
||||||
<ComboBoxItem x:Uid="Awake_IndefiniteKeepAwake" />
|
<ComboBoxItem x:Uid="Awake_IndefiniteKeepAwakeSelector" />
|
||||||
<ComboBoxItem x:Uid="Awake_TemporaryKeepAwake" />
|
<ComboBoxItem x:Uid="Awake_TemporaryKeepAwakeSelector" />
|
||||||
|
<ComboBoxItem x:Uid="Awake_ExpirableKeepAwakeSelector" />
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
</labs:SettingsCard>
|
</labs:SettingsCard>
|
||||||
|
|
||||||
<labs:SettingsCard
|
<labs:SettingsCard
|
||||||
x:Uid="Awake_TimeBeforeAwake"
|
x:Uid="Awake_ExpirationSettingsCard"
|
||||||
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily}, Glyph=}"
|
||||||
|
Visibility="{x:Bind ViewModel.IsExpirationConfigurationEnabled, Mode=OneWay}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<DatePicker Date="{x:Bind ViewModel.ExpirationDateTime, Mode=TwoWay}"></DatePicker>
|
||||||
|
<TimePicker Margin="8,0,0,0" Time="{x:Bind ViewModel.ExpirationTime, Mode=TwoWay}" ClockIdentifier="24HourClock"></TimePicker>
|
||||||
|
</StackPanel>
|
||||||
|
</labs:SettingsCard>
|
||||||
|
|
||||||
|
<labs:SettingsCard
|
||||||
|
x:Uid="Awake_IntervalSettingsCard"
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily}, Glyph=}"
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily}, Glyph=}"
|
||||||
Visibility="{x:Bind ViewModel.IsTimeConfigurationEnabled, Mode=OneWay}">
|
Visibility="{x:Bind ViewModel.IsTimeConfigurationEnabled, Mode=OneWay}">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<NumberBox
|
<NumberBox
|
||||||
x:Uid="Awake_TemporaryKeepAwake_Hours"
|
x:Uid="Awake_IntervalHoursInput"
|
||||||
Width="96"
|
Width="96"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
LargeChange="5"
|
LargeChange="5"
|
||||||
Minimum="0"
|
Minimum="0"
|
||||||
SmallChange="1"
|
SmallChange="1"
|
||||||
SpinButtonPlacementMode="Compact"
|
SpinButtonPlacementMode="Compact"
|
||||||
Value="{x:Bind ViewModel.Hours, Mode=TwoWay}" />
|
Value="{x:Bind ViewModel.IntervalHours, Mode=TwoWay}" />
|
||||||
<NumberBox
|
<NumberBox
|
||||||
x:Uid="Awake_TemporaryKeepAwake_Minutes"
|
x:Uid="Awake_IntervalMinutesInput"
|
||||||
Width="96"
|
Width="96"
|
||||||
Margin="8,0,0,0"
|
Margin="8,0,0,0"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
@@ -76,16 +89,15 @@
|
|||||||
Minimum="0"
|
Minimum="0"
|
||||||
SmallChange="1"
|
SmallChange="1"
|
||||||
SpinButtonPlacementMode="Compact"
|
SpinButtonPlacementMode="Compact"
|
||||||
Value="{x:Bind ViewModel.Minutes, Mode=TwoWay}" />
|
Value="{x:Bind ViewModel.IntervalMinutes, Mode=TwoWay}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</labs:SettingsCard>
|
</labs:SettingsCard>
|
||||||
|
|
||||||
<labs:SettingsCard
|
<labs:SettingsCard
|
||||||
x:Uid="Awake_EnableDisplayKeepAwake"
|
x:Uid="Awake_DisplaySettingsCard"
|
||||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily}, Glyph=}"
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily}, Glyph=}"
|
||||||
IsEnabled="{x:Bind ViewModel.IsScreenConfigurationPossibleEnabled, Mode=OneWay}">
|
IsEnabled="{x:Bind ViewModel.IsScreenConfigurationPossibleEnabled, Mode=OneWay}">
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
x:Uid="ToggleSwitch"
|
|
||||||
IsOn="{x:Bind ViewModel.KeepDisplayOn, Mode=TwoWay}" />
|
IsOn="{x:Bind ViewModel.KeepDisplayOn, Mode=TwoWay}" />
|
||||||
</labs:SettingsCard>
|
</labs:SettingsCard>
|
||||||
</controls:SettingsGroup>
|
</controls:SettingsGroup>
|
||||||
@@ -99,7 +111,7 @@
|
|||||||
</controls:SettingsPageControl.PrimaryLinks>
|
</controls:SettingsPageControl.PrimaryLinks>
|
||||||
<controls:SettingsPageControl.SecondaryLinks>
|
<controls:SettingsPageControl.SecondaryLinks>
|
||||||
<controls:PageLink
|
<controls:PageLink
|
||||||
Link="https://Awake.den.dev"
|
Link="https://awake.den.dev"
|
||||||
Text="Den Delimarsky's work on creating Awake" />
|
Text="Den Delimarsky's work on creating Awake" />
|
||||||
</controls:SettingsPageControl.SecondaryLinks>
|
</controls:SettingsPageControl.SecondaryLinks>
|
||||||
</controls:SettingsPageControl>
|
</controls:SettingsPageControl>
|
||||||
|
|||||||
@@ -2,25 +2,184 @@
|
|||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Abstractions;
|
||||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||||
using Microsoft.PowerToys.Settings.UI.Library;
|
using Microsoft.PowerToys.Settings.UI.Library;
|
||||||
|
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||||
|
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||||
using Microsoft.PowerToys.Settings.UI.ViewModels;
|
using Microsoft.PowerToys.Settings.UI.ViewModels;
|
||||||
|
using Microsoft.UI.Dispatching;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using PowerToys.GPOWrapper;
|
||||||
|
|
||||||
namespace Microsoft.PowerToys.Settings.UI.Views
|
namespace Microsoft.PowerToys.Settings.UI.Views
|
||||||
{
|
{
|
||||||
public sealed partial class AwakePage : Page, IRefreshablePage
|
public sealed partial class AwakePage : Page, IRefreshablePage
|
||||||
{
|
{
|
||||||
|
private readonly string _appName = "Awake";
|
||||||
|
private readonly SettingsUtils _settingsUtils;
|
||||||
|
|
||||||
|
private readonly SettingsRepository<GeneralSettings> _generalSettingsRepository;
|
||||||
|
private readonly SettingsRepository<AwakeSettings> _moduleSettingsRepository;
|
||||||
|
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
private readonly IFileSystemWatcher _fileSystemWatcher;
|
||||||
|
|
||||||
|
private readonly DispatcherQueue _dispatcherQueue;
|
||||||
|
|
||||||
|
private readonly Func<string, int> _sendConfigMsg;
|
||||||
|
|
||||||
private AwakeViewModel ViewModel { get; set; }
|
private AwakeViewModel ViewModel { get; set; }
|
||||||
|
|
||||||
public AwakePage()
|
public AwakePage()
|
||||||
{
|
{
|
||||||
var settingsUtils = new SettingsUtils();
|
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||||
ViewModel = new AwakeViewModel(SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), SettingsRepository<AwakeSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage);
|
_fileSystem = new FileSystem();
|
||||||
|
_settingsUtils = new SettingsUtils();
|
||||||
|
_sendConfigMsg = ShellPage.SendDefaultIPCMessage;
|
||||||
|
|
||||||
|
ViewModel = new AwakeViewModel();
|
||||||
|
ViewModel.PropertyChanged += ViewModel_PropertyChanged;
|
||||||
|
|
||||||
|
_generalSettingsRepository = SettingsRepository<GeneralSettings>.GetInstance(_settingsUtils);
|
||||||
|
_moduleSettingsRepository = SettingsRepository<AwakeSettings>.GetInstance(_settingsUtils);
|
||||||
|
|
||||||
|
// We load the view model settings first.
|
||||||
|
LoadSettings(_generalSettingsRepository, _moduleSettingsRepository);
|
||||||
|
|
||||||
DataContext = ViewModel;
|
DataContext = ViewModel;
|
||||||
|
|
||||||
|
var settingsPath = _settingsUtils.GetSettingsFilePath(_appName);
|
||||||
|
|
||||||
|
_fileSystemWatcher = _fileSystem.FileSystemWatcher.CreateNew();
|
||||||
|
_fileSystemWatcher.Path = _fileSystem.Path.GetDirectoryName(settingsPath);
|
||||||
|
_fileSystemWatcher.Filter = _fileSystem.Path.GetFileName(settingsPath);
|
||||||
|
_fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.CreationTime;
|
||||||
|
_fileSystemWatcher.Changed += Settings_Changed;
|
||||||
|
_fileSystemWatcher.EnableRaisingEvents = true;
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggered whenever a view model property changes. This is done in addition to the baked-in view model changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// TODO: The logic here needs to be optimized since doing string comparison on values is not ideal.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="sender">Sender of the change.</param>
|
||||||
|
/// <param name="e">Property parameter.</param>
|
||||||
|
private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_sendConfigMsg != null)
|
||||||
|
{
|
||||||
|
if (e.PropertyName == "IsEnabled")
|
||||||
|
{
|
||||||
|
if (ViewModel.IsEnabled != _generalSettingsRepository.SettingsConfig.Enabled.Awake)
|
||||||
|
{
|
||||||
|
_generalSettingsRepository.SettingsConfig.Enabled.Awake = ViewModel.IsEnabled;
|
||||||
|
|
||||||
|
var generalSettingsMessage = new OutGoingGeneralSettings(_generalSettingsRepository.SettingsConfig).ToString();
|
||||||
|
|
||||||
|
Logger.LogInfo($"Saved general settings from Awake page.");
|
||||||
|
_sendConfigMsg(generalSettingsMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ViewModel.ModuleSettings != null)
|
||||||
|
{
|
||||||
|
SndAwakeSettings currentSettings = new(_moduleSettingsRepository.SettingsConfig);
|
||||||
|
SndModuleSettings<SndAwakeSettings> csIpcMessage = new(currentSettings);
|
||||||
|
|
||||||
|
SndAwakeSettings outSettings = new(ViewModel.ModuleSettings);
|
||||||
|
SndModuleSettings<SndAwakeSettings> outIpcMessage = new(outSettings);
|
||||||
|
|
||||||
|
string csMessage = csIpcMessage.ToJsonString();
|
||||||
|
string outMessage = outIpcMessage.ToJsonString();
|
||||||
|
|
||||||
|
if (!csMessage.Equals(outMessage))
|
||||||
|
{
|
||||||
|
Logger.LogInfo($"Saved Awake settings from Awake page.");
|
||||||
|
_sendConfigMsg(outMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadSettings(ISettingsRepository<GeneralSettings> generalSettingsRepository, ISettingsRepository<AwakeSettings> moduleSettingsRepository)
|
||||||
|
{
|
||||||
|
if (generalSettingsRepository != null)
|
||||||
|
{
|
||||||
|
if (moduleSettingsRepository != null)
|
||||||
|
{
|
||||||
|
UpdateViewModelSettings(moduleSettingsRepository.SettingsConfig, generalSettingsRepository.SettingsConfig);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(moduleSettingsRepository));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(generalSettingsRepository));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateViewModelSettings(AwakeSettings awakeSettings, GeneralSettings generalSettings)
|
||||||
|
{
|
||||||
|
if (awakeSettings != null)
|
||||||
|
{
|
||||||
|
if (generalSettings != null)
|
||||||
|
{
|
||||||
|
ViewModel.IsEnabled = generalSettings.Enabled.Awake;
|
||||||
|
ViewModel.ModuleSettings = (AwakeSettings)awakeSettings.Clone();
|
||||||
|
|
||||||
|
UpdateEnabledState(generalSettings.Enabled.Awake);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(generalSettings));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(awakeSettings));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the tool enablement state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recommendedState">The state that is recommended for the tool, but can be overridden if a GPO policy is in place.</param>
|
||||||
|
private void UpdateEnabledState(bool recommendedState)
|
||||||
|
{
|
||||||
|
var enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredAwakeEnabledValue();
|
||||||
|
|
||||||
|
if (enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
|
||||||
|
{
|
||||||
|
// Get the enabled state from GPO.
|
||||||
|
ViewModel.IsEnabledGpoConfigured = true;
|
||||||
|
ViewModel.IsEnabled = enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ViewModel.IsEnabled = recommendedState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Settings_Changed(object sender, FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
bool taskAdded = _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () =>
|
||||||
|
{
|
||||||
|
_moduleSettingsRepository.ReloadSettings();
|
||||||
|
LoadSettings(_generalSettingsRepository, _moduleSettingsRepository);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void RefreshEnabledState()
|
public void RefreshEnabledState()
|
||||||
{
|
{
|
||||||
ViewModel.RefreshEnabledState();
|
ViewModel.RefreshEnabledState();
|
||||||
|
|||||||
Reference in New Issue
Block a user