From e8ad4fa80488f01dfffe463b8d948233da0ece01 Mon Sep 17 00:00:00 2001 From: Den Delimarsky Date: Tue, 30 Jul 2024 08:08:37 -0700 Subject: [PATCH] [Awake]Fix DAISY build issues (#34054) This PR addresses some post-merge issues caught by @davidegiacometti, including: 1. Separator in the context menu shown when not running from inside PowerToys. 2. "Keep display on" setting not persisting across switches between modes. 3. Awake not launching in standalone mode. Additionally: 1. Exits are now properly handled in **timed** and **expirable** keep-awake modes when running standalone. This ensures that Awake exists after completion and doesn't switch to an in-actionable passive mode. 2. Tray tooltips now cover how much time is left on the timer. 3. Fixes #29354 4. Avoids a nasty memory leak because of re-instantiating of `Icon` objects for every tray update. 5. Adds DPI awareness to the context menu (#16123) --- src/modules/awake/Awake/Awake.csproj | 1 + .../awake/Awake/Core/ExtensionMethods.cs | 12 ++ src/modules/awake/Awake/Core/Manager.cs | 85 +++++++---- src/modules/awake/Awake/Core/TrayHelper.cs | 138 +++++++++++------- src/modules/awake/Awake/Program.cs | 50 ++++--- .../Awake/Properties/Resources.Designer.cs | 38 ++++- .../awake/Awake/Properties/Resources.resx | 18 ++- src/modules/awake/Awake/app.manifest | 8 + 8 files changed, 246 insertions(+), 104 deletions(-) create mode 100644 src/modules/awake/Awake/app.manifest diff --git a/src/modules/awake/Awake/Awake.csproj b/src/modules/awake/Awake/Awake.csproj index 1ea7ab2b39b..604841f4f6b 100644 --- a/src/modules/awake/Awake/Awake.csproj +++ b/src/modules/awake/Awake/Awake.csproj @@ -35,6 +35,7 @@ PowerToys.GPOWrapper $(OutDir) false + app.manifest diff --git a/src/modules/awake/Awake/Core/ExtensionMethods.cs b/src/modules/awake/Awake/Core/ExtensionMethods.cs index f86e9812fa7..4435e6e428c 100644 --- a/src/modules/awake/Awake/Core/ExtensionMethods.cs +++ b/src/modules/awake/Awake/Core/ExtensionMethods.cs @@ -19,5 +19,17 @@ public static void AddRange(this ICollection target, IEnumerable source target.Add(element); } } + + public static string ToHumanReadableString(this TimeSpan timeSpan) + { + // Get days, hours, minutes, and seconds from the TimeSpan + int days = timeSpan.Days; + int hours = timeSpan.Hours; + int minutes = timeSpan.Minutes; + int seconds = timeSpan.Seconds; + + // Format the string based on the presence of days, hours, minutes, and seconds + return $"{days:D2}{Properties.Resources.AWAKE_LABEL_DAYS} {hours:D2}{Properties.Resources.AWAKE_LABEL_HOURS} {minutes:D2}{Properties.Resources.AWAKE_LABEL_MINUTES} {seconds:D2}{Properties.Resources.AWAKE_LABEL_SECONDS}"; + } } } diff --git a/src/modules/awake/Awake/Core/Manager.cs b/src/modules/awake/Awake/Core/Manager.cs index 473a4f21472..c5217dcdba3 100644 --- a/src/modules/awake/Awake/Core/Manager.cs +++ b/src/modules/awake/Awake/Core/Manager.cs @@ -39,6 +39,12 @@ public class Manager private static readonly BlockingCollection _stateQueue; + // Core icons used for the tray + private static readonly Icon _timedIcon = new(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets/Awake/timed.ico")); + private static readonly Icon _expirableIcon = new(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets/Awake/expirable.ico")); + private static readonly Icon _indefiniteIcon = new(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets/Awake/indefinite.ico")); + private static readonly Icon _disabledIcon = new(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets/Awake/disabled.ico")); + private static CancellationTokenSource _tokenSource; private static SettingsUtils? _moduleSettings; @@ -135,7 +141,7 @@ internal static void SetIndefiniteKeepAwake(bool keepDisplayOn = false) _stateQueue.Add(ComputeAwakeState(keepDisplayOn)); - TrayHelper.SetShellIcon(TrayHelper.HiddenWindowHandle, $"{Constants.FullAppName} [{Resources.AWAKE_TRAY_TEXT_INDEFINITE}]", new Icon(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets/Awake/indefinite.ico")), TrayIconAction.Update); + TrayHelper.SetShellIcon(TrayHelper.HiddenWindowHandle, $"{Constants.FullAppName} [{Resources.AWAKE_TRAY_TEXT_INDEFINITE}]", _indefiniteIcon, TrayIconAction.Update); if (IsUsingPowerToysConfig) { @@ -172,14 +178,23 @@ internal static void SetExpirableKeepAwake(DateTimeOffset expireAt, bool keepDis Logger.LogInfo($"Starting expirable log for {expireAt}"); _stateQueue.Add(ComputeAwakeState(keepDisplayOn)); - TrayHelper.SetShellIcon(TrayHelper.HiddenWindowHandle, $"{Constants.FullAppName} [{Resources.AWAKE_TRAY_TEXT_EXPIRATION}]", new Icon(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets/Awake/expirable.ico")), TrayIconAction.Update); + TrayHelper.SetShellIcon(TrayHelper.HiddenWindowHandle, $"{Constants.FullAppName} [{Resources.AWAKE_TRAY_TEXT_EXPIRATION} - {expireAt}]", _expirableIcon, TrayIconAction.Update); Observable.Timer(expireAt - DateTimeOffset.Now).Subscribe( _ => { Logger.LogInfo($"Completed expirable keep-awake."); CancelExistingThread(); - SetPassiveKeepAwake(); + + if (IsUsingPowerToysConfig) + { + SetPassiveKeepAwake(); + } + else + { + Logger.LogInfo("Exiting after expirable keep awake."); + CompleteExit(Environment.ExitCode); + } }, _tokenSource.Token); } @@ -224,16 +239,40 @@ internal static void SetTimedKeepAwake(uint seconds, bool keepDisplayOn = true) Logger.LogInfo($"Timed keep awake started for {seconds} seconds."); _stateQueue.Add(ComputeAwakeState(keepDisplayOn)); - TrayHelper.SetShellIcon(TrayHelper.HiddenWindowHandle, $"{Constants.FullAppName} [{Resources.AWAKE_TRAY_TEXT_TIMED}]", new Icon(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets/Awake/timed.ico")), TrayIconAction.Update); + TrayHelper.SetShellIcon(TrayHelper.HiddenWindowHandle, $"{Constants.FullAppName} [{Resources.AWAKE_TRAY_TEXT_TIMED}]", _timedIcon, TrayIconAction.Update); - Observable.Timer(TimeSpan.FromSeconds(seconds)).Subscribe( - _ => - { - Logger.LogInfo($"Completed timed thread."); - CancelExistingThread(); - SetPassiveKeepAwake(); - }, - _tokenSource.Token); + var timerObservable = Observable.Timer(TimeSpan.FromSeconds(seconds)); + var intervalObservable = Observable.Interval(TimeSpan.FromSeconds(1)).TakeUntil(timerObservable); + + var combinedObservable = Observable.CombineLatest(intervalObservable, timerObservable.StartWith(0), (elapsedSeconds, _) => elapsedSeconds + 1); + + combinedObservable.Subscribe( + elapsedSeconds => + { + var timeRemaining = seconds - (uint)elapsedSeconds; + if (timeRemaining >= 0) + { + TrayHelper.SetShellIcon(TrayHelper.HiddenWindowHandle, $"{Constants.FullAppName} [{Resources.AWAKE_TRAY_TEXT_TIMED}]\n{TimeSpan.FromSeconds(timeRemaining).ToHumanReadableString()}", _timedIcon, TrayIconAction.Update); + } + }, + () => + { + Console.WriteLine("Completed timed thread."); + CancelExistingThread(); + + if (IsUsingPowerToysConfig) + { + // If we're using PowerToys settings, we need to make sure that + // we just switch over the Passive Keep-Awake. + SetPassiveKeepAwake(); + } + else + { + Logger.LogInfo("Exiting after timed keep-awake."); + CompleteExit(Environment.ExitCode); + } + }, + _tokenSource.Token); if (IsUsingPowerToysConfig) { @@ -264,9 +303,7 @@ internal static void SetTimedKeepAwake(uint seconds, bool keepDisplayOn = true) /// Performs a clean exit from Awake. /// /// Exit code to exit with. - /// Exit signal tracking the state. - /// Determines whether to force exit and post a quitting message. - internal static void CompleteExit(int exitCode, ManualResetEvent? exitSignal, bool force = false) + internal static void CompleteExit(int exitCode) { SetPassiveKeepAwake(updateSettings: false); @@ -277,22 +314,12 @@ internal static void CompleteExit(int exitCode, ManualResetEvent? exitSignal, bo // Close the message window that we used for the tray. Bridge.SendMessage(TrayHelper.HiddenWindowHandle, Native.Constants.WM_CLOSE, 0, 0); - } - - if (force) - { - Bridge.PostQuitMessage(exitCode); - } - try - { - exitSignal?.Set(); Bridge.DestroyWindow(TrayHelper.HiddenWindowHandle); } - catch (Exception ex) - { - Logger.LogError($"Exit signal error ${ex}"); - } + + Bridge.PostQuitMessage(exitCode); + Environment.Exit(exitCode); } /// @@ -350,7 +377,7 @@ internal static void SetPassiveKeepAwake(bool updateSettings = true) CancelExistingThread(); - TrayHelper.SetShellIcon(TrayHelper.HiddenWindowHandle, $"{Constants.FullAppName} [{Resources.AWAKE_TRAY_TEXT_OFF}]", new Icon(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets/Awake/disabled.ico")), TrayIconAction.Update); + TrayHelper.SetShellIcon(TrayHelper.HiddenWindowHandle, $"{Constants.FullAppName} [{Resources.AWAKE_TRAY_TEXT_OFF}]", _disabledIcon, TrayIconAction.Update); if (IsUsingPowerToysConfig && updateSettings) { diff --git a/src/modules/awake/Awake/Core/TrayHelper.cs b/src/modules/awake/Awake/Core/TrayHelper.cs index eff05e7c5b8..00000a431f9 100644 --- a/src/modules/awake/Awake/Core/TrayHelper.cs +++ b/src/modules/awake/Awake/Core/TrayHelper.cs @@ -28,7 +28,6 @@ namespace Awake.Core internal static class TrayHelper { private static NotifyIconData _notifyIconData; - private static ManualResetEvent? _exitSignal; private static IntPtr _trayMenu; @@ -44,41 +43,49 @@ static TrayHelper() HiddenWindowHandle = IntPtr.Zero; } - public static void InitializeTray(string text, Icon icon, ManualResetEvent? exitSignal) + public static void InitializeTray(string text, Icon icon) { - _exitSignal = exitSignal; - CreateHiddenWindow(icon, text); } private static void ShowContextMenu(IntPtr hWnd) { - Bridge.SetForegroundWindow(hWnd); + if (TrayMenu != IntPtr.Zero) + { + Bridge.SetForegroundWindow(hWnd); - // Get the handle to the context menu associated with the tray icon - IntPtr hMenu = TrayMenu; + // Get the handle to the context menu associated with the tray icon + IntPtr hMenu = TrayMenu; - // Get the current cursor position - Bridge.GetCursorPos(out Models.Point cursorPos); + // Get the current cursor position + Bridge.GetCursorPos(out Models.Point cursorPos); - Bridge.ScreenToClient(hWnd, ref cursorPos); + Bridge.ScreenToClient(hWnd, ref cursorPos); - MenuInfo menuInfo = new() + MenuInfo menuInfo = new() + { + CbSize = (uint)Marshal.SizeOf(typeof(MenuInfo)), + FMask = Native.Constants.MIM_STYLE, + DwStyle = Native.Constants.MNS_AUTO_DISMISS, + }; + Bridge.SetMenuInfo(hMenu, ref menuInfo); + + // Display the context menu at the cursor position + Bridge.TrackPopupMenuEx( + hMenu, + Native.Constants.TPM_LEFT_ALIGN | Native.Constants.TPM_BOTTOMALIGN | Native.Constants.TPM_LEFT_BUTTON, + cursorPos.X, + cursorPos.Y, + hWnd, + IntPtr.Zero); + } + else { - CbSize = (uint)Marshal.SizeOf(typeof(MenuInfo)), - FMask = Native.Constants.MIM_STYLE, - DwStyle = Native.Constants.MNS_AUTO_DISMISS, - }; - Bridge.SetMenuInfo(hMenu, ref menuInfo); - - // Display the context menu at the cursor position - Bridge.TrackPopupMenuEx( - hMenu, - Native.Constants.TPM_LEFT_ALIGN | Native.Constants.TPM_BOTTOMALIGN | Native.Constants.TPM_LEFT_BUTTON, - cursorPos.X, - cursorPos.Y, - hWnd, - IntPtr.Zero); + // Tray menu was not initialized. Log the issue. + // This is normal when operating in "standalone mode" - that is, detached + // from the PowerToys configuration file. + Logger.LogError("Tried to create a context menu while the TrayMenu object is a null pointer. Normal when used in standalone mode."); + } } private static void CreateHiddenWindow(Icon icon, string text) @@ -159,30 +166,40 @@ internal static void SetShellIcon(IntPtr hWnd, string text, Icon? icon, TrayIcon break; } - _notifyIconData = action == TrayIconAction.Add || action == TrayIconAction.Update - ? new NotifyIconData + if (action == TrayIconAction.Add || action == TrayIconAction.Update) + { + _notifyIconData = new NotifyIconData { CbSize = Marshal.SizeOf(typeof(NotifyIconData)), HWnd = hWnd, UId = 1000, UFlags = Native.Constants.NIF_ICON | Native.Constants.NIF_TIP | Native.Constants.NIF_MESSAGE, UCallbackMessage = (int)Native.Constants.WM_USER, - HIcon = icon!.Handle, + HIcon = icon?.Handle ?? IntPtr.Zero, SzTip = text, - } - : new NotifyIconData + }; + } + else if (action == TrayIconAction.Delete) + { + _notifyIconData = new NotifyIconData { CbSize = Marshal.SizeOf(typeof(NotifyIconData)), HWnd = hWnd, UId = 1000, UFlags = 0, }; + } if (!Bridge.Shell_NotifyIcon(message, ref _notifyIconData)) { int errorCode = Marshal.GetLastWin32Error(); throw new Win32Exception(errorCode, $"Failed to change tray icon. Action: {action} and error code: {errorCode}"); } + + if (action == TrayIconAction.Delete) + { + _notifyIconData = default; + } } private static void RunMessageLoop() @@ -218,32 +235,47 @@ private static int WndProc(IntPtr hWnd, uint message, IntPtr wParam, IntPtr lPar switch (targetCommandIndex) { case (uint)TrayCommands.TC_EXIT: - Manager.CompleteExit(0, _exitSignal, true); - break; + { + Manager.CompleteExit(Environment.ExitCode); + break; + } + case (uint)TrayCommands.TC_DISPLAY_SETTING: - Manager.SetDisplay(); - break; + { + Manager.SetDisplay(); + break; + } + case (uint)TrayCommands.TC_MODE_INDEFINITE: - Manager.SetIndefiniteKeepAwake(); - break; + { + AwakeSettings settings = Manager.ModuleSettings!.GetSettings(Constants.AppName); + Manager.SetIndefiniteKeepAwake(keepDisplayOn: settings.Properties.KeepDisplayOn); + break; + } + case (uint)TrayCommands.TC_MODE_PASSIVE: - Manager.SetPassiveKeepAwake(); - break; + { + Manager.SetPassiveKeepAwake(); + break; + } + default: - if (targetCommandIndex >= trayCommandsSize) { - AwakeSettings settings = Manager.ModuleSettings!.GetSettings(Constants.AppName); - if (settings.Properties.CustomTrayTimes.Count == 0) + if (targetCommandIndex >= trayCommandsSize) { - settings.Properties.CustomTrayTimes.AddRange(Manager.GetDefaultTrayOptions()); + AwakeSettings settings = Manager.ModuleSettings!.GetSettings(Constants.AppName); + if (settings.Properties.CustomTrayTimes.Count == 0) + { + settings.Properties.CustomTrayTimes.AddRange(Manager.GetDefaultTrayOptions()); + } + + int index = (int)targetCommandIndex - (int)TrayCommands.TC_TIME; + uint targetTime = (uint)settings.Properties.CustomTrayTimes.ElementAt(index).Value; + Manager.SetTimedKeepAwake(targetTime, keepDisplayOn: settings.Properties.KeepDisplayOn); } - int index = (int)targetCommandIndex - (int)TrayCommands.TC_TIME; - uint targetTime = (uint)settings.Properties.CustomTrayTimes.ElementAt(index).Value; - Manager.SetTimedKeepAwake(targetTime); + break; } - - break; } break; @@ -300,7 +332,7 @@ public static void SetTray(bool keepDisplayOn, AwakeMode mode, Dictionary trayT } } - private static void CreateAwakeTimeSubMenu(Dictionary trayTimeShortcuts) + private static void CreateAwakeTimeSubMenu(Dictionary trayTimeShortcuts, bool isChecked = false) { var awakeTimeMenu = Bridge.CreatePopupMenu(); for (int i = 0; i < trayTimeShortcuts.Count; i++) @@ -359,7 +395,7 @@ private static void CreateAwakeTimeSubMenu(Dictionary trayTimeShort Bridge.InsertMenu(awakeTimeMenu, (uint)i, Native.Constants.MF_BYPOSITION | Native.Constants.MF_STRING, (uint)TrayCommands.TC_TIME + (uint)i, trayTimeShortcuts.ElementAt(i).Key); } - Bridge.InsertMenu(TrayMenu, 0, Native.Constants.MF_BYPOSITION | Native.Constants.MF_POPUP, (uint)awakeTimeMenu, Resources.AWAKE_KEEP_ON_INTERVAL); + Bridge.InsertMenu(TrayMenu, 0, Native.Constants.MF_BYPOSITION | Native.Constants.MF_POPUP | (isChecked == true ? Native.Constants.MF_CHECKED : Native.Constants.MF_UNCHECKED), (uint)awakeTimeMenu, Resources.AWAKE_KEEP_ON_INTERVAL); } private static void InsertAwakeModeMenuItems(AwakeMode mode) diff --git a/src/modules/awake/Awake/Program.cs b/src/modules/awake/Awake/Program.cs index 09a660a3505..8f36e7a14b3 100644 --- a/src/modules/awake/Awake/Program.cs +++ b/src/modules/awake/Awake/Program.cs @@ -26,8 +26,6 @@ namespace Awake { internal sealed class Program { - private static readonly ManualResetEvent _exitSignal = new(false); - private static Mutex? _mutex; private static FileSystemWatcher? _watcher; private static SettingsUtils? _settingsUtils; @@ -47,6 +45,8 @@ internal sealed class Program internal static readonly string[] AliasesPidOption = ["--pid", "-p"]; internal static readonly string[] AliasesExpireAtOption = ["--expire-at", "-e"]; + private static readonly Icon _defaultAwakeIcon = new(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets/Awake/awake.ico")); + private static int Main(string[] args) { _settingsUtils = new SettingsUtils(); @@ -54,15 +54,17 @@ private static int Main(string[] args) Logger.InitializeLogger(Path.Combine("\\", Core.Constants.AppName, "Logs")); + AppDomain.CurrentDomain.UnhandledException += AwakeUnhandledExceptionCatcher; + if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredAwakeEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { - Exit("PowerToys.Awake tried to start with a group policy setting that disables the tool. Please contact your system 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); return 0; } if (!instantiated) { - Exit(Core.Constants.AppName + " is already running! Exiting the application.", 1, _exitSignal, true); + Exit(Core.Constants.AppName + " is already running! Exiting the application.", 1); } Logger.LogInfo($"Launching {Core.Constants.AppName}..."); @@ -129,18 +131,26 @@ private static int Main(string[] args) return rootCommand.InvokeAsync(args).Result; } + private static void AwakeUnhandledExceptionCatcher(object sender, UnhandledExceptionEventArgs e) + { + if (e.ExceptionObject is Exception exception) + { + Logger.LogError(exception.ToString()); + Logger.LogError(exception.StackTrace); + } + } + private static bool ExitHandler(ControlType ctrlType) { Logger.LogInfo($"Exited through handler with control type: {ctrlType}"); - Exit(Resources.AWAKE_EXIT_MESSAGE, Environment.ExitCode, _exitSignal); + Exit(Resources.AWAKE_EXIT_MESSAGE, Environment.ExitCode); return false; } - private static void Exit(string message, int exitCode, ManualResetEvent exitSignal, bool force = false) + private static void Exit(string message, int exitCode) { Logger.LogInfo(message); - - Manager.CompleteExit(exitCode, exitSignal, force); + Manager.CompleteExit(exitCode); } private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, uint timeLimit, int pid, string expireAt) @@ -169,6 +179,15 @@ private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, // Start the monitor thread that will be used to track the current state. Manager.StartMonitor(); + TrayHelper.InitializeTray(Core.Constants.FullAppName, _defaultAwakeIcon); + + var eventHandle = new EventWaitHandle(false, EventResetMode.ManualReset, interop.Constants.AwakeExitEvent()); + new Thread(() => + { + WaitHandle.WaitAny([eventHandle]); + Exit(Resources.AWAKE_EXIT_SIGNAL_MESSAGE, 0); + }).Start(); + if (usePtConfig) { // Configuration file is used, therefore we disregard any other command-line parameter @@ -177,17 +196,6 @@ private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, try { - var eventHandle = new EventWaitHandle(false, EventResetMode.ManualReset, interop.Constants.AwakeExitEvent()); - new Thread(() => - { - if (WaitHandle.WaitAny([_exitSignal, eventHandle]) == 1) - { - Exit(Resources.AWAKE_EXIT_SIGNAL_MESSAGE, 0, _exitSignal, true); - } - }).Start(); - - TrayHelper.InitializeTray(Core.Constants.FullAppName, new Icon(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets/Awake/awake.ico")), _exitSignal); - string? settingsPath = _settingsUtils!.GetSettingsFilePath(Core.Constants.AppName); Logger.LogInfo($"Reading configuration file: {settingsPath}"); @@ -245,11 +253,9 @@ private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, RunnerHelper.WaitForPowerToysRunner(pid, () => { Logger.LogInfo($"Triggered PID-based exit handler for PID {pid}."); - Exit(Resources.AWAKE_EXIT_BINDING_HOOK_MESSAGE, 0, _exitSignal, true); + Exit(Resources.AWAKE_EXIT_BINDING_HOOK_MESSAGE, 0); }); } - - _exitSignal.WaitOne(); } private static void ScaffoldConfiguration(string settingsPath) diff --git a/src/modules/awake/Awake/Properties/Resources.Designer.cs b/src/modules/awake/Awake/Properties/Resources.Designer.cs index 2a8ac0a8cfa..0cdaf23605c 100644 --- a/src/modules/awake/Awake/Properties/Resources.Designer.cs +++ b/src/modules/awake/Awake/Properties/Resources.Designer.cs @@ -195,6 +195,42 @@ internal static string AWAKE_KEEP_UNTIL_EXPIRATION { } } + /// + /// Looks up a localized string similar to d. + /// + internal static string AWAKE_LABEL_DAYS { + get { + return ResourceManager.GetString("AWAKE_LABEL_DAYS", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h. + /// + internal static string AWAKE_LABEL_HOURS { + get { + return ResourceManager.GetString("AWAKE_LABEL_HOURS", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to m. + /// + internal static string AWAKE_LABEL_MINUTES { + get { + return ResourceManager.GetString("AWAKE_LABEL_MINUTES", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to s. + /// + internal static string AWAKE_LABEL_SECONDS { + get { + return ResourceManager.GetString("AWAKE_LABEL_SECONDS", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} minutes. /// @@ -241,7 +277,7 @@ internal static string AWAKE_TRAY_TEXT_OFF { } /// - /// Looks up a localized string similar to Timed. + /// Looks up a localized string similar to Interval. /// internal static string AWAKE_TRAY_TEXT_TIMED { get { diff --git a/src/modules/awake/Awake/Properties/Resources.resx b/src/modules/awake/Awake/Properties/Resources.resx index abb0f8ca36b..7ef19501287 100644 --- a/src/modules/awake/Awake/Properties/Resources.resx +++ b/src/modules/awake/Awake/Properties/Resources.resx @@ -187,6 +187,22 @@ Passive - Timed + Interval + + + d + Used to display number of days in the system tray tooltip. + + + h + Used to display number of hours in the system tray tooltip. + + + m + Used to display number of minutes in the system tray tooltip. + + + s + Used to display number of seconds in the system tray tooltip. \ No newline at end of file diff --git a/src/modules/awake/Awake/app.manifest b/src/modules/awake/Awake/app.manifest new file mode 100644 index 00000000000..a3d1e52638e --- /dev/null +++ b/src/modules/awake/Awake/app.manifest @@ -0,0 +1,8 @@ + + + + + true + + + \ No newline at end of file