Fixes for PowerToys Awake (#12215)
* Change how background threads operate This should reduce CPU usage. * Well there is just no need for the console here * Need to keep the full name sanitized * Update the changes to console handling * Updating how we handle constants * Fix process termination logic * Making some tweaks to the termination logic * Update how the process is tracked * Updating how application closing is done for Awake. * Update with explicit types * Fix high CPU usage for timed keep awake. * Logging typo fix. * Update some of the timer logic. * Fix variable naming for consistency * Cleanup the C++ interface * Cleanup the code a bit This change introduces support for: - Proper event handling across the two apps (Awake and runner). - Removal of unnecessary functions. * Cleaning up the code even further * Remove unnecessary functions
This commit is contained in:
parent
5027a55e7f
commit
579881002e
|
@ -186,5 +186,9 @@ public
|
|||
static String ^ ShowColorPickerSharedEvent() {
|
||||
return gcnew String(CommonSharedConstants::SHOW_COLOR_PICKER_SHARED_EVENT);
|
||||
}
|
||||
|
||||
static String ^ AwakeExitEvent() {
|
||||
return gcnew String(CommonSharedConstants::AWAKE_EXIT_EVENT);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ namespace CommonSharedConstants
|
|||
|
||||
const wchar_t FANCY_ZONES_EDITOR_TOGGLE_EVENT[] = L"Local\\FancyZones-ToggleEditorEvent-1e174338-06a3-472b-874d-073b21c62f14";
|
||||
|
||||
// Path to the event used by Awake
|
||||
const wchar_t AWAKE_EXIT_EVENT[] = L"Local\\PowerToysAwakeExitEvent-c0d5e305-35fc-4fb5-83ec-f6070cfaf7fe";
|
||||
|
||||
// Max DWORD for key code to disable keys.
|
||||
const DWORD VK_DISABLED = 0x100;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,18 @@ namespace Awake.Core
|
|||
ES_SYSTEM_REQUIRED = 0x00000001,
|
||||
}
|
||||
|
||||
// See: https://docs.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,
|
||||
}
|
||||
|
||||
public delegate bool ConsoleEventHandler(ControlType ctrlType);
|
||||
|
||||
/// <summary>
|
||||
/// Helper class that allows talking to Win32 APIs without having to rely on PInvoke in other parts
|
||||
/// of the codebase.
|
||||
|
@ -37,6 +49,10 @@ namespace Awake.Core
|
|||
private static CancellationToken _threadToken;
|
||||
|
||||
private static Task? _runnerThread;
|
||||
private static System.Timers.Timer _timedLoopTimer;
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool SetConsoleCtrlHandler(ConsoleEventHandler handler, bool add);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
|
||||
|
@ -63,10 +79,16 @@ namespace Awake.Core
|
|||
|
||||
static APIHelper()
|
||||
{
|
||||
_timedLoopTimer = new System.Timers.Timer();
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
public static void SetConsoleControlHandler(ConsoleEventHandler handler, bool addHandler)
|
||||
{
|
||||
SetConsoleCtrlHandler(handler, addHandler);
|
||||
}
|
||||
|
||||
public static void AllocateConsole()
|
||||
{
|
||||
_log.Debug("Bootstrapping the console allocation routine.");
|
||||
|
@ -139,7 +161,7 @@ namespace Awake.Core
|
|||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_log.Info("Confirmed background thread cancellation when setting passive keep awake.");
|
||||
_log.Info("Confirmed background thread cancellation when disabling explicit keep awake.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,7 +178,7 @@ namespace Awake.Core
|
|||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_log.Info("Confirmed background thread cancellation when setting indefinite keep awake.");
|
||||
_log.Info("Confirmed background thread cancellation when setting timed keep awake.");
|
||||
}
|
||||
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
|
@ -223,14 +245,25 @@ namespace Awake.Core
|
|||
if (success)
|
||||
{
|
||||
_log.Info($"Initiated temporary keep awake in background thread: {GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
|
||||
var startTime = DateTime.UtcNow;
|
||||
while (DateTime.UtcNow - startTime < TimeSpan.FromSeconds(Math.Abs(seconds)))
|
||||
|
||||
_timedLoopTimer = new System.Timers.Timer(seconds * 1000);
|
||||
_timedLoopTimer.Elapsed += (s, e) =>
|
||||
{
|
||||
if (_threadToken.IsCancellationRequested)
|
||||
{
|
||||
_threadToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
}
|
||||
_tokenSource.Cancel();
|
||||
|
||||
_timedLoopTimer.Stop();
|
||||
};
|
||||
|
||||
_timedLoopTimer.Disposed += (s, e) =>
|
||||
{
|
||||
_log.Info("Old timer disposed.");
|
||||
};
|
||||
|
||||
_timedLoopTimer.Start();
|
||||
|
||||
WaitHandle.WaitAny(new[] { _threadToken.WaitHandle });
|
||||
_timedLoopTimer.Stop();
|
||||
_timedLoopTimer.Dispose();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
|
12
src/modules/awake/Awake/Core/InternalConstants.cs
Normal file
12
src/modules/awake/Awake/Core/InternalConstants.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
|
||||
{
|
||||
internal static class InternalConstants
|
||||
{
|
||||
internal const string AppName = "Awake";
|
||||
internal const string FullAppName = "PowerToys " + AppName;
|
||||
}
|
||||
}
|
|
@ -6,8 +6,10 @@ using System;
|
|||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using NLog;
|
||||
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
|
@ -16,32 +18,43 @@ namespace Awake.Core
|
|||
{
|
||||
internal static class TrayHelper
|
||||
{
|
||||
private static NotifyIcon? trayIcon;
|
||||
private static readonly Logger _log;
|
||||
|
||||
private static NotifyIcon TrayIcon { get => trayIcon; set => trayIcon = value; }
|
||||
private static NotifyIcon? _trayIcon;
|
||||
|
||||
private static SettingsUtils? moduleSettings;
|
||||
private static NotifyIcon TrayIcon { get => _trayIcon; set => _trayIcon = value; }
|
||||
|
||||
private static SettingsUtils ModuleSettings { get => moduleSettings; set => moduleSettings = value; }
|
||||
private static SettingsUtils? _moduleSettings;
|
||||
|
||||
private static SettingsUtils ModuleSettings { get => _moduleSettings; set => _moduleSettings = value; }
|
||||
|
||||
static TrayHelper()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
TrayIcon = new NotifyIcon();
|
||||
ModuleSettings = new SettingsUtils();
|
||||
}
|
||||
|
||||
public static void InitializeTray(string text, Icon icon, ContextMenuStrip? contextMenu = null)
|
||||
{
|
||||
System.Threading.Tasks.Task.Factory.StartNew(
|
||||
Task.Factory.StartNew(
|
||||
(tray) =>
|
||||
{
|
||||
((NotifyIcon?)tray).Text = text;
|
||||
((NotifyIcon?)tray).Icon = icon;
|
||||
((NotifyIcon?)tray).ContextMenuStrip = contextMenu;
|
||||
((NotifyIcon?)tray).Visible = true;
|
||||
{
|
||||
((NotifyIcon?)tray).Text = text;
|
||||
((NotifyIcon?)tray).Icon = icon;
|
||||
((NotifyIcon?)tray).ContextMenuStrip = contextMenu;
|
||||
((NotifyIcon?)tray).Visible = true;
|
||||
|
||||
Application.Run();
|
||||
}, TrayIcon);
|
||||
_log.Info("Setting up the tray.");
|
||||
Application.Run();
|
||||
_log.Info("Tray setup complete.");
|
||||
}, TrayIcon);
|
||||
}
|
||||
|
||||
public static void ClearTray()
|
||||
{
|
||||
TrayIcon.Icon = null;
|
||||
TrayIcon.Dispose();
|
||||
}
|
||||
|
||||
internal static void SetTray(string text, AwakeSettings settings)
|
||||
|
@ -50,10 +63,10 @@ namespace Awake.Core
|
|||
text,
|
||||
settings.Properties.KeepDisplayOn,
|
||||
settings.Properties.Mode,
|
||||
PassiveKeepAwakeCallback(text),
|
||||
IndefiniteKeepAwakeCallback(text),
|
||||
TimedKeepAwakeCallback(text),
|
||||
KeepDisplayOnCallback(text),
|
||||
PassiveKeepAwakeCallback(InternalConstants.AppName),
|
||||
IndefiniteKeepAwakeCallback(InternalConstants.AppName),
|
||||
TimedKeepAwakeCallback(InternalConstants.AppName),
|
||||
KeepDisplayOnCallback(InternalConstants.AppName),
|
||||
ExitCallback());
|
||||
}
|
||||
|
||||
|
@ -61,7 +74,7 @@ namespace Awake.Core
|
|||
{
|
||||
return () =>
|
||||
{
|
||||
Environment.Exit(0);
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -153,28 +166,21 @@ namespace Awake.Core
|
|||
|
||||
public static void SetTray(string text, bool keepDisplayOn, AwakeMode mode, Action passiveKeepAwakeCallback, Action indefiniteKeepAwakeCallback, Action<uint, uint> timedKeepAwakeCallback, Action keepDisplayOnCallback, Action exitCallback)
|
||||
{
|
||||
var contextMenuStrip = new ContextMenuStrip();
|
||||
ContextMenuStrip? contextMenuStrip = new ContextMenuStrip();
|
||||
|
||||
// Main toolstrip.
|
||||
var operationContextMenu = new ToolStripMenuItem
|
||||
ToolStripMenuItem? operationContextMenu = new ToolStripMenuItem
|
||||
{
|
||||
Text = "Mode",
|
||||
};
|
||||
|
||||
// No keep-awake menu item.
|
||||
var passiveMenuItem = new ToolStripMenuItem
|
||||
ToolStripMenuItem? passiveMenuItem = new ToolStripMenuItem
|
||||
{
|
||||
Text = "Off (Passive)",
|
||||
};
|
||||
|
||||
if (mode == AwakeMode.PASSIVE)
|
||||
{
|
||||
passiveMenuItem.Checked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
passiveMenuItem.Checked = false;
|
||||
}
|
||||
passiveMenuItem.Checked = mode == AwakeMode.PASSIVE;
|
||||
|
||||
passiveMenuItem.Click += (e, s) =>
|
||||
{
|
||||
|
@ -183,19 +189,12 @@ namespace Awake.Core
|
|||
};
|
||||
|
||||
// Indefinite keep-awake menu item.
|
||||
var indefiniteMenuItem = new ToolStripMenuItem
|
||||
ToolStripMenuItem? indefiniteMenuItem = new ToolStripMenuItem
|
||||
{
|
||||
Text = "Keep awake indefinitely",
|
||||
};
|
||||
|
||||
if (mode == AwakeMode.INDEFINITE)
|
||||
{
|
||||
indefiniteMenuItem.Checked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
indefiniteMenuItem.Checked = false;
|
||||
}
|
||||
indefiniteMenuItem.Checked = mode == AwakeMode.INDEFINITE;
|
||||
|
||||
indefiniteMenuItem.Click += (e, s) =>
|
||||
{
|
||||
|
@ -203,18 +202,12 @@ namespace Awake.Core
|
|||
indefiniteKeepAwakeCallback();
|
||||
};
|
||||
|
||||
var displayOnMenuItem = new ToolStripMenuItem
|
||||
ToolStripMenuItem? displayOnMenuItem = new ToolStripMenuItem
|
||||
{
|
||||
Text = "Keep screen on",
|
||||
};
|
||||
if (keepDisplayOn)
|
||||
{
|
||||
displayOnMenuItem.Checked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
displayOnMenuItem.Checked = false;
|
||||
}
|
||||
|
||||
displayOnMenuItem.Checked = keepDisplayOn;
|
||||
|
||||
displayOnMenuItem.Click += (e, s) =>
|
||||
{
|
||||
|
@ -223,43 +216,40 @@ namespace Awake.Core
|
|||
};
|
||||
|
||||
// Timed keep-awake menu item
|
||||
var timedMenuItem = new ToolStripMenuItem
|
||||
ToolStripMenuItem? timedMenuItem = new ToolStripMenuItem
|
||||
{
|
||||
Text = "Keep awake temporarily",
|
||||
};
|
||||
if (mode == AwakeMode.TIMED)
|
||||
{
|
||||
timedMenuItem.Checked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
timedMenuItem.Checked = false;
|
||||
}
|
||||
|
||||
var halfHourMenuItem = new ToolStripMenuItem
|
||||
timedMenuItem.Checked = mode == AwakeMode.TIMED;
|
||||
|
||||
ToolStripMenuItem? halfHourMenuItem = new ToolStripMenuItem
|
||||
{
|
||||
Text = "30 minutes",
|
||||
};
|
||||
|
||||
halfHourMenuItem.Click += (e, s) =>
|
||||
{
|
||||
// User is setting the keep-awake to 30 minutes.
|
||||
timedKeepAwakeCallback(0, 30);
|
||||
};
|
||||
|
||||
var oneHourMenuItem = new ToolStripMenuItem
|
||||
ToolStripMenuItem? oneHourMenuItem = new ToolStripMenuItem
|
||||
{
|
||||
Text = "1 hour",
|
||||
};
|
||||
|
||||
oneHourMenuItem.Click += (e, s) =>
|
||||
{
|
||||
// User is setting the keep-awake to 1 hour.
|
||||
timedKeepAwakeCallback(1, 0);
|
||||
};
|
||||
|
||||
var twoHoursMenuItem = new ToolStripMenuItem
|
||||
ToolStripMenuItem? twoHoursMenuItem = new ToolStripMenuItem
|
||||
{
|
||||
Text = "2 hours",
|
||||
};
|
||||
|
||||
twoHoursMenuItem.Click += (e, s) =>
|
||||
{
|
||||
// User is setting the keep-awake to 2 hours.
|
||||
|
@ -267,10 +257,11 @@ namespace Awake.Core
|
|||
};
|
||||
|
||||
// Exit menu item.
|
||||
var exitContextMenu = new ToolStripMenuItem
|
||||
ToolStripMenuItem? exitContextMenu = new ToolStripMenuItem
|
||||
{
|
||||
Text = "Exit",
|
||||
};
|
||||
|
||||
exitContextMenu.Click += (e, s) =>
|
||||
{
|
||||
// User is setting the keep-awake to 2 hours.
|
||||
|
|
|
@ -15,6 +15,7 @@ using System.Reflection;
|
|||
using System.Threading;
|
||||
using System.Windows;
|
||||
using Awake.Core;
|
||||
using interop;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using NLog;
|
||||
|
@ -27,8 +28,6 @@ namespace Awake
|
|||
internal class Program
|
||||
{
|
||||
private static Mutex? _mutex = null;
|
||||
private const string AppName = "Awake";
|
||||
private const string FullAppName = "PowerToys " + AppName;
|
||||
private static FileSystemWatcher? _watcher = null;
|
||||
private static SettingsUtils? _settingsUtils = null;
|
||||
|
||||
|
@ -36,17 +35,25 @@ namespace Awake
|
|||
|
||||
private static Logger? _log;
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
private static ConsoleEventHandler _handler;
|
||||
#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)
|
||||
{
|
||||
bool instantiated;
|
||||
LockMutex = new Mutex(true, AppName, out instantiated);
|
||||
// Log initialization needs to always happen before we test whether
|
||||
// only one instance of Awake is running.
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
LockMutex = new Mutex(true, InternalConstants.AppName, out bool instantiated);
|
||||
|
||||
if (!instantiated)
|
||||
{
|
||||
ForceExit(AppName + " is already running! Exiting the application.", 1);
|
||||
Exit(InternalConstants.AppName + " is already running! Exiting the application.", 1, true);
|
||||
}
|
||||
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_settingsUtils = new SettingsUtils();
|
||||
|
||||
_log.Info("Launching PowerToys Awake...");
|
||||
|
@ -56,7 +63,7 @@ namespace Awake
|
|||
|
||||
_log.Info("Parsing parameters...");
|
||||
|
||||
var configOption = new Option<bool>(
|
||||
Option<bool>? configOption = new Option<bool>(
|
||||
aliases: new[] { "--use-pt-config", "-c" },
|
||||
getDefaultValue: () => false,
|
||||
description: "Specifies whether PowerToys Awake will be using the PowerToys configuration file for managing the state.")
|
||||
|
@ -69,7 +76,7 @@ namespace Awake
|
|||
|
||||
configOption.Required = false;
|
||||
|
||||
var displayOption = new Option<bool>(
|
||||
Option<bool>? displayOption = new Option<bool>(
|
||||
aliases: new[] { "--display-on", "-d" },
|
||||
getDefaultValue: () => true,
|
||||
description: "Determines whether the display should be kept awake.")
|
||||
|
@ -82,7 +89,7 @@ namespace Awake
|
|||
|
||||
displayOption.Required = false;
|
||||
|
||||
var timeOption = new Option<uint>(
|
||||
Option<uint>? timeOption = new Option<uint>(
|
||||
aliases: new[] { "--time-limit", "-t" },
|
||||
getDefaultValue: () => 0,
|
||||
description: "Determines the interval, in seconds, during which the computer is kept awake.")
|
||||
|
@ -95,7 +102,7 @@ namespace Awake
|
|||
|
||||
timeOption.Required = false;
|
||||
|
||||
var pidOption = new Option<int>(
|
||||
Option<int>? pidOption = new Option<int>(
|
||||
aliases: new[] { "--pid", "-p" },
|
||||
getDefaultValue: () => 0,
|
||||
description: "Bind the execution of PowerToys Awake to another process.")
|
||||
|
@ -108,7 +115,7 @@ namespace Awake
|
|||
|
||||
pidOption.Required = false;
|
||||
|
||||
var rootCommand = new RootCommand
|
||||
RootCommand? rootCommand = new RootCommand
|
||||
{
|
||||
configOption,
|
||||
displayOption,
|
||||
|
@ -116,7 +123,7 @@ namespace Awake
|
|||
pidOption,
|
||||
};
|
||||
|
||||
rootCommand.Description = AppName;
|
||||
rootCommand.Description = InternalConstants.AppName;
|
||||
|
||||
rootCommand.Handler = CommandHandler.Create<bool, bool, uint, int>(HandleCommandLineArguments);
|
||||
|
||||
|
@ -125,14 +132,38 @@ namespace Awake
|
|||
return rootCommand.InvokeAsync(args).Result;
|
||||
}
|
||||
|
||||
private static void ForceExit(string message, int exitCode)
|
||||
private static bool ExitHandler(ControlType ctrlType)
|
||||
{
|
||||
_log.Info($"Exited through handler with control type: {ctrlType}");
|
||||
|
||||
Exit("Exiting from the internal termination handler.", Environment.ExitCode);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void Exit(string message, int exitCode, bool force = false)
|
||||
{
|
||||
_log.Info(message);
|
||||
Environment.Exit(exitCode);
|
||||
|
||||
APIHelper.SetNoKeepAwake();
|
||||
TrayHelper.ClearTray();
|
||||
|
||||
// Because we are running a message loop for the tray, we can't just use Environment.Exit,
|
||||
// but have to make sure that we properly send the termination message.
|
||||
bool cwResult = System.Diagnostics.Process.GetCurrentProcess().CloseMainWindow();
|
||||
_log.Info($"Request to close main window status: {cwResult}");
|
||||
|
||||
if (force)
|
||||
{
|
||||
Environment.Exit(exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, uint timeLimit, int pid)
|
||||
{
|
||||
_handler += new ConsoleEventHandler(ExitHandler);
|
||||
APIHelper.SetConsoleControlHandler(_handler, true);
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
_log.Info("No PID specified. Allocating console...");
|
||||
|
@ -150,11 +181,18 @@ namespace Awake
|
|||
// and instead watch for changes in the file.
|
||||
try
|
||||
{
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
TrayHelper.InitializeTray(FullAppName, new Icon(Application.GetResourceStream(new Uri("/Images/Awake.ico", UriKind.Relative)).Stream));
|
||||
#pragma warning restore CS8604 // Possible null reference argument.
|
||||
new Thread(() =>
|
||||
{
|
||||
EventWaitHandle? eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.AwakeExitEvent());
|
||||
if (eventHandle.WaitOne())
|
||||
{
|
||||
Exit("Received a signal to end the process. Making sure we quit...", 0, true);
|
||||
}
|
||||
}).Start();
|
||||
|
||||
var settingsPath = _settingsUtils.GetSettingsFilePath(AppName);
|
||||
TrayHelper.InitializeTray(InternalConstants.FullAppName, new Icon(Application.GetResourceStream(new Uri("/Images/Awake.ico", UriKind.Relative)).Stream));
|
||||
|
||||
string? settingsPath = _settingsUtils.GetSettingsFilePath(InternalConstants.AppName);
|
||||
_log.Info($"Reading configuration file: {settingsPath}");
|
||||
|
||||
_watcher = new FileSystemWatcher
|
||||
|
@ -165,22 +203,22 @@ namespace Awake
|
|||
Filter = Path.GetFileName(settingsPath),
|
||||
};
|
||||
|
||||
var changedObservable = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
|
||||
IObservable<System.Reactive.EventPattern<FileSystemEventArgs>>? changedObservable = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
|
||||
h => _watcher.Changed += h,
|
||||
h => _watcher.Changed -= h);
|
||||
|
||||
var createdObservable = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
|
||||
IObservable<System.Reactive.EventPattern<FileSystemEventArgs>>? createdObservable = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
|
||||
cre => _watcher.Created += cre,
|
||||
cre => _watcher.Created -= cre);
|
||||
|
||||
var mergedObservable = Observable.Merge(changedObservable, createdObservable);
|
||||
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(FullAppName, new AwakeSettings());
|
||||
TrayHelper.SetTray(InternalConstants.FullAppName, new AwakeSettings());
|
||||
|
||||
// Initially the file might not be updated, so we need to start processing
|
||||
// settings right away.
|
||||
|
@ -188,14 +226,14 @@ namespace Awake
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var errorString = $"There was a problem with the configuration file. Make sure it exists.\n{ex.Message}";
|
||||
string? errorString = $"There was a problem with the configuration file. Make sure it exists.\n{ex.Message}";
|
||||
_log.Info(errorString);
|
||||
_log.Debug(errorString);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var mode = timeLimit <= 0 ? AwakeMode.INDEFINITE : AwakeMode.TIMED;
|
||||
AwakeMode mode = timeLimit <= 0 ? AwakeMode.INDEFINITE : AwakeMode.TIMED;
|
||||
|
||||
if (mode == AwakeMode.INDEFINITE)
|
||||
{
|
||||
|
@ -207,22 +245,19 @@ namespace Awake
|
|||
}
|
||||
}
|
||||
|
||||
var exitSignal = new ManualResetEvent(false);
|
||||
if (pid != 0)
|
||||
{
|
||||
RunnerHelper.WaitForPowerToysRunner(pid, () =>
|
||||
{
|
||||
exitSignal.Set();
|
||||
Environment.Exit(0);
|
||||
Exit("Terminating from PowerToys binding hook.", 0, true);
|
||||
});
|
||||
}
|
||||
|
||||
exitSignal.WaitOne();
|
||||
_exitSignal.WaitOne();
|
||||
}
|
||||
|
||||
private static void SetupIndefiniteKeepAwake(bool displayOn)
|
||||
{
|
||||
// Indefinite keep awake.
|
||||
APIHelper.SetIndefiniteKeepAwake(LogCompletedKeepAwakeThread, LogUnexpectedOrCancelledKeepAwakeThreadCompletion, displayOn);
|
||||
}
|
||||
|
||||
|
@ -237,7 +272,7 @@ namespace Awake
|
|||
{
|
||||
try
|
||||
{
|
||||
AwakeSettings settings = _settingsUtils.GetSettings<AwakeSettings>(AppName);
|
||||
AwakeSettings settings = _settingsUtils.GetSettings<AwakeSettings>(InternalConstants.AppName);
|
||||
|
||||
if (settings != null)
|
||||
{
|
||||
|
@ -251,14 +286,12 @@ namespace Awake
|
|||
|
||||
case AwakeMode.INDEFINITE:
|
||||
{
|
||||
// Indefinite keep awake.
|
||||
SetupIndefiniteKeepAwake(settings.Properties.KeepDisplayOn);
|
||||
break;
|
||||
}
|
||||
|
||||
case AwakeMode.TIMED:
|
||||
{
|
||||
// Timed keep-awake.
|
||||
uint computedTime = (settings.Properties.Hours * 60 * 60) + (settings.Properties.Minutes * 60);
|
||||
SetupTimedKeepAwake(computedTime, settings.Properties.KeepDisplayOn);
|
||||
|
||||
|
@ -267,25 +300,25 @@ namespace Awake
|
|||
|
||||
default:
|
||||
{
|
||||
var errorMessage = "Unknown mode of operation. Check config file.";
|
||||
string? errorMessage = "Unknown mode of operation. Check config file.";
|
||||
_log.Info(errorMessage);
|
||||
_log.Debug(errorMessage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TrayHelper.SetTray(FullAppName, settings);
|
||||
TrayHelper.SetTray(InternalConstants.FullAppName, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorMessage = "Settings are null.";
|
||||
string? errorMessage = "Settings are null.";
|
||||
_log.Info(errorMessage);
|
||||
_log.Debug(errorMessage);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var errorMessage = $"There was a problem reading the configuration file. Error: {ex.GetType()} {ex.Message}";
|
||||
string? errorMessage = $"There was a problem reading the configuration file. Error: {ex.GetType()} {ex.Message}";
|
||||
_log.Info(errorMessage);
|
||||
_log.Debug(errorMessage);
|
||||
}
|
||||
|
@ -307,7 +340,7 @@ namespace Awake
|
|||
|
||||
private static void LogUnexpectedOrCancelledKeepAwakeThreadCompletion()
|
||||
{
|
||||
var errorMessage = "The keep-awake thread was terminated early.";
|
||||
string? errorMessage = "The keep-awake thread was terminated early.";
|
||||
_log.Info(errorMessage);
|
||||
_log.Debug(errorMessage);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <common/utils/winapi_error.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <set>
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
|
@ -33,34 +34,23 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
// The PowerToy name that will be shown in the settings.
|
||||
const static wchar_t* MODULE_NAME = L"Awake";
|
||||
|
||||
// Add a description that will we shown in the module settings page.
|
||||
const static wchar_t* MODULE_DESC = L"A module that keeps your computer awake on-demand.";
|
||||
|
||||
// Implement the PowerToy Module Interface and all the required methods.
|
||||
class Awake : public PowertoyModuleIface
|
||||
{
|
||||
std::wstring app_name;
|
||||
|
||||
// Contains the non localized key of the powertoy
|
||||
std::wstring app_key;
|
||||
|
||||
private:
|
||||
// The PowerToy state.
|
||||
bool m_enabled = false;
|
||||
|
||||
HANDLE m_hProcess;
|
||||
|
||||
HANDLE send_telemetry_event;
|
||||
|
||||
// Handle to event used to invoke PowerToys Awake
|
||||
HANDLE m_hInvokeEvent;
|
||||
PROCESS_INFORMATION p_info;
|
||||
|
||||
bool is_process_running()
|
||||
{
|
||||
return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT;
|
||||
return WaitForSingleObject(p_info.hProcess, 0) == WAIT_TIMEOUT;
|
||||
}
|
||||
|
||||
void launch_process()
|
||||
|
@ -69,26 +59,22 @@ private:
|
|||
unsigned long powertoys_pid = GetCurrentProcessId();
|
||||
|
||||
std::wstring executable_args = L"--use-pt-config --pid " + std::to_wstring(powertoys_pid);
|
||||
std::wstring application_path = L"modules\\Awake\\PowerToys.Awake.exe";
|
||||
std::wstring full_command_path = application_path + L" " + executable_args.data();
|
||||
Logger::trace(L"PowerToys Awake launching with parameters: " + executable_args);
|
||||
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
|
||||
sei.lpFile = L"modules\\Awake\\PowerToys.Awake.exe";
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
sei.lpParameters = executable_args.data();
|
||||
if (!ShellExecuteExW(&sei))
|
||||
STARTUPINFO info = { sizeof(info) };
|
||||
|
||||
if (!CreateProcess(application_path.c_str(), full_command_path.data(), NULL, NULL, true, NULL, NULL, NULL, &info, &p_info))
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
std::wstring message = L"PowerToys Awake failed to start with error = ";
|
||||
std::wstring message = L"PowerToys Awake failed to start with error: ";
|
||||
message += std::to_wstring(error);
|
||||
Logger::error(message);
|
||||
}
|
||||
|
||||
m_hProcess = sei.hProcess;
|
||||
}
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
Awake()
|
||||
{
|
||||
app_name = GET_RESOURCE_STRING(IDS_AWAKE_NAME);
|
||||
|
@ -99,37 +85,31 @@ public:
|
|||
Logger::info("Launcher object is constructing");
|
||||
};
|
||||
|
||||
// Destroy the powertoy and free memory
|
||||
virtual void destroy() override
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
// Return the display name of the powertoy, this will be cached by the runner
|
||||
virtual const wchar_t* get_name() override
|
||||
{
|
||||
return MODULE_NAME;
|
||||
}
|
||||
|
||||
// Return JSON with the configuration options.
|
||||
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
|
||||
{
|
||||
HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
|
||||
|
||||
// Create a Settings object.
|
||||
PowerToysSettings::Settings settings(hinstance, get_name());
|
||||
settings.set_description(MODULE_DESC);
|
||||
|
||||
return settings.serialize_to_buffer(buffer, buffer_size);
|
||||
}
|
||||
|
||||
// Return the non localized key of the powertoy, this will be cached by the runner
|
||||
virtual const wchar_t* get_key() override
|
||||
{
|
||||
return app_key.c_str();
|
||||
}
|
||||
|
||||
// Called by the runner to pass the updated settings values as a serialized JSON.
|
||||
virtual void set_config(const wchar_t* config) override
|
||||
{
|
||||
try
|
||||
|
@ -139,7 +119,7 @@ public:
|
|||
PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
|
||||
|
||||
// If you don't need to do any custom processing of the settings, proceed
|
||||
// to persists the values calling:
|
||||
// to persists the values.
|
||||
values.save_to_settings_file();
|
||||
}
|
||||
catch (std::exception&)
|
||||
|
@ -160,15 +140,36 @@ public:
|
|||
{
|
||||
if (m_enabled)
|
||||
{
|
||||
Logger::trace(L"Disabling Awake...");
|
||||
ResetEvent(send_telemetry_event);
|
||||
ResetEvent(m_hInvokeEvent);
|
||||
TerminateProcess(m_hProcess, 1);
|
||||
|
||||
auto exitEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::AWAKE_EXIT_EVENT);
|
||||
if (!exitEvent)
|
||||
{
|
||||
Logger::warn(L"Failed to create exit event for PowerToys Awake. {}", get_last_error_or_default(GetLastError()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::trace(L"Signaled exit event for PowerToys Awake.");
|
||||
if (!SetEvent(exitEvent))
|
||||
{
|
||||
Logger::warn(L"Failed to signal exit event for PowerToys Awake. {}", get_last_error_or_default(GetLastError()));
|
||||
|
||||
// For some reason, we couldn't process the signal correctly, so we still
|
||||
// need to terminate the Awake process.
|
||||
TerminateProcess(p_info.hProcess, 1);
|
||||
}
|
||||
|
||||
ResetEvent(exitEvent);
|
||||
CloseHandle(exitEvent);
|
||||
CloseHandle(p_info.hProcess);
|
||||
}
|
||||
}
|
||||
|
||||
m_enabled = false;
|
||||
}
|
||||
|
||||
// Returns if the powertoys is enabled
|
||||
virtual bool is_enabled() override
|
||||
{
|
||||
return m_enabled;
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
|
|||
OnPropertyChanged(nameof(IsEnabled));
|
||||
OnPropertyChanged(nameof(IsTimeConfigurationEnabled));
|
||||
|
||||
var outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
|
||||
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
|
||||
SendConfigMSG(outgoing.ToString());
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
|
|||
SndAwakeSettings outsettings = new SndAwakeSettings(Settings);
|
||||
SndModuleSettings<SndAwakeSettings> ipcMessage = new SndModuleSettings<SndAwakeSettings>(outsettings);
|
||||
|
||||
var targetMessage = ipcMessage.ToJsonString();
|
||||
string targetMessage = ipcMessage.ToJsonString();
|
||||
SendConfigMSG(targetMessage);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue