From af4ec9ad8bc40be13a0358fba12f4904c16eab8e Mon Sep 17 00:00:00 2001 From: Den Delimarsky <1389609+dend@users.noreply.github.com> Date: Sun, 9 May 2021 22:25:52 -0700 Subject: [PATCH] Change how settings and tray are handled. --- .../espresso/Espresso/Core/SettingsHelper.cs | 41 ------ .../espresso/Espresso/Core/TrayHelper.cs | 103 +++++++++++-- src/modules/espresso/Espresso/Espresso.csproj | 1 + .../Espresso/Models/EspressoSettingsModel.cs | 52 ------- src/modules/espresso/Espresso/Program.cs | 139 ++++++++---------- 5 files changed, 152 insertions(+), 184 deletions(-) delete mode 100644 src/modules/espresso/Espresso/Core/SettingsHelper.cs delete mode 100644 src/modules/espresso/Espresso/Models/EspressoSettingsModel.cs diff --git a/src/modules/espresso/Espresso/Core/SettingsHelper.cs b/src/modules/espresso/Espresso/Core/SettingsHelper.cs deleted file mode 100644 index 3a3aabd2f9..0000000000 --- a/src/modules/espresso/Espresso/Core/SettingsHelper.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; - -namespace Espresso.Shell.Core -{ - public class SettingsHelper - { - const int ERROR_SHARING_VIOLATION = 32; - const int ERROR_LOCK_VIOLATION = 33; - - public static FileStream? GetSettingsFile(string path, int retries) - { - for (int i = 0; i < retries; i++) - { - try - { - FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None); - return fileStream; - } - catch (IOException ex) - { - var errorCode = Marshal.GetHRForException(ex) & ((1 << 16) - 1); - if (errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION) - { - Console.WriteLine("There was another process using the file, so couldn't pick the settings up."); - } - - Thread.Sleep(50); - } - } - - return null; - } - } -} diff --git a/src/modules/espresso/Espresso/Core/TrayHelper.cs b/src/modules/espresso/Espresso/Core/TrayHelper.cs index 55bde4447b..1d0472ee6e 100644 --- a/src/modules/espresso/Espresso/Core/TrayHelper.cs +++ b/src/modules/espresso/Espresso/Core/TrayHelper.cs @@ -2,36 +2,76 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Espresso.Shell.Models; +using Microsoft.PowerToys.Settings.UI.Library; +using Newtonsoft.Json; using System; using System.Drawing; -using System.Threading.Tasks; using System.Windows.Forms; +#pragma warning disable CS8602 // Dereference of a possibly null reference. +#pragma warning disable CS8603 // Possible null reference return. + namespace Espresso.Shell.Core { internal static class TrayHelper { - static NotifyIcon trayIcon; + static NotifyIcon? trayIcon; + private static NotifyIcon TrayIcon { get => trayIcon; set => trayIcon = value; } + + static SettingsUtils? moduleSettings; + private static SettingsUtils ModuleSettings { get => moduleSettings; set => moduleSettings = value; } + static TrayHelper() { - trayIcon = new NotifyIcon(); + TrayIcon = new NotifyIcon(); + ModuleSettings = new SettingsUtils(); } - private static void InitializeTrayIcon(string text, Icon icon, ContextMenuStrip contextMenu) + public static void InitializeTray(string text, Icon icon, ContextMenuStrip? contextMenu) { - trayIcon.Text = text; - trayIcon.Icon = icon; - trayIcon.ContextMenuStrip = contextMenu; - trayIcon.Visible = true; + System.Threading.Tasks.Task.Factory.StartNew((tray) => + { + ((NotifyIcon?)tray).Text = text; + ((NotifyIcon?)tray).Icon = icon; + ((NotifyIcon?)tray).ContextMenuStrip = contextMenu; + ((NotifyIcon?)tray).Visible = true; - Application.Run(); + Application.Run(); + }, TrayIcon); } - internal static void InitializeEspressoTray(string text, EspressoMode mode, bool keepDisplayOn, Action indefiniteSelectionCallback, Action timedSelectionCallback) + internal static void SetTray(string text, EspressoSettings settings) + { + SetTray(text, settings.Properties.KeepDisplayOn.Value, settings.Properties.Mode, settings.Properties.Hours.Value, settings.Properties.Minutes.Value, + () => { + // Set indefinite keep awake. + var currentSettings = ModuleSettings.GetSettings(text); + currentSettings.Properties.Mode = EspressoMode.INDEFINITE; + ModuleSettings.SaveSettings(JsonConvert.SerializeObject(currentSettings), text); + }, + () => { + // Set timed keep awake. + var currentSettings = ModuleSettings.GetSettings(text); + currentSettings.Properties.Mode = EspressoMode.TIMED; + currentSettings.Properties.Hours.Value = settings.Properties.Hours.Value; + currentSettings.Properties.Minutes.Value = settings.Properties.Minutes.Value; + + ModuleSettings.SaveSettings(JsonConvert.SerializeObject(currentSettings), text); + }, + () => + { + // Just changing the display mode. + var currentSettings = ModuleSettings.GetSettings(text); + currentSettings.Properties.KeepDisplayOn = settings.Properties.KeepDisplayOn; + + ModuleSettings.SaveSettings(JsonConvert.SerializeObject(currentSettings), text); + }); + } + + internal static void SetTray(string text, bool keepDisplayOn, EspressoMode mode, int hours, int minutes, Action indefiniteKeepAwakeCallback, Action timedKeepAwakeCallback, Action keepDisplayOnCallback) { var contextMenuStrip = new ContextMenuStrip(); - + // Main toolstrip. var operationContextMenu = new ToolStripMenuItem(); operationContextMenu.Text = "Mode"; @@ -39,10 +79,28 @@ namespace Espresso.Shell.Core // Indefinite keep-awake menu item. var indefiniteMenuItem = new ToolStripMenuItem(); indefiniteMenuItem.Text = "Indefinite"; + if (mode == EspressoMode.INDEFINITE) { indefiniteMenuItem.Checked = true; } + indefiniteMenuItem.Click += (e, s) => + { + // User opted to set the mode to indefinite, so we need to write new settings. + indefiniteKeepAwakeCallback(); + }; + + var displayOnMenuItem = new ToolStripMenuItem(); + displayOnMenuItem.Text = "Keep display on"; + if (keepDisplayOn) + { + displayOnMenuItem.Checked = true; + } + displayOnMenuItem.Click += (e, s) => + { + // User opted to set the display mode directly. + keepDisplayOnCallback(); + }; // Timed keep-awake menu item var timedMenuItem = new ToolStripMenuItem(); @@ -50,12 +108,27 @@ namespace Espresso.Shell.Core var halfHourMenuItem = new ToolStripMenuItem(); halfHourMenuItem.Text = "30 minutes"; + halfHourMenuItem.Click += (e, s) => + { + // User is setting the keep-awake to 30 minutes. + timedKeepAwakeCallback(); + }; var oneHourMenuItem = new ToolStripMenuItem(); oneHourMenuItem.Text = "1 hour"; + oneHourMenuItem.Click += (e, s) => + { + // User is setting the keep-awake to 1 hour. + timedKeepAwakeCallback(); + }; var twoHoursMenuItem = new ToolStripMenuItem(); twoHoursMenuItem.Text = "2 hours"; + twoHoursMenuItem.Click += (e, s) => + { + // User is setting the keep-awake to 2 hours. + timedKeepAwakeCallback(); + }; timedMenuItem.DropDownItems.Add(halfHourMenuItem); timedMenuItem.DropDownItems.Add(oneHourMenuItem); @@ -63,12 +136,12 @@ namespace Espresso.Shell.Core operationContextMenu.DropDownItems.Add(indefiniteMenuItem); operationContextMenu.DropDownItems.Add(timedMenuItem); + operationContextMenu.DropDownItems.Add(new ToolStripSeparator()); + operationContextMenu.DropDownItems.Add(displayOnMenuItem); contextMenuStrip.Items.Add(operationContextMenu); -#pragma warning disable CS8604 // Possible null reference argument. - Task.Factory.StartNew(() => InitializeTrayIcon(text, APIHelper.Extract("shell32.dll", 12, true), contextMenuStrip)); -#pragma warning restore CS8604 // Possible null reference argument. + TrayIcon.ContextMenuStrip = contextMenuStrip; } } } diff --git a/src/modules/espresso/Espresso/Espresso.csproj b/src/modules/espresso/Espresso/Espresso.csproj index 4643acd75e..65a7be85cc 100644 --- a/src/modules/espresso/Espresso/Espresso.csproj +++ b/src/modules/espresso/Espresso/Espresso.csproj @@ -49,6 +49,7 @@ + diff --git a/src/modules/espresso/Espresso/Models/EspressoSettingsModel.cs b/src/modules/espresso/Espresso/Models/EspressoSettingsModel.cs deleted file mode 100644 index db9263dac5..0000000000 --- a/src/modules/espresso/Espresso/Models/EspressoSettingsModel.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Newtonsoft.Json; - -namespace Espresso.Shell.Models -{ - public enum EspressoMode - { - INDEFINITE = 0, - TIMED = 1, - } - - public class EspressoSettingsModel - { - [JsonProperty("properties")] - public Properties? Properties { get; set; } - [JsonProperty("name")] - public string? Name { get; set; } - [JsonProperty("version")] - public string? Version { get; set; } - } - - public class Properties - { - [JsonProperty("espresso_keep_display_on")] - public KeepDisplayOn? KeepDisplayOn { get; set; } - [JsonProperty("espresso_mode")] - public EspressoMode Mode { get; set; } - [JsonProperty("espresso_hours")] - public Hours? Hours { get; set; } - [JsonProperty("espresso_minutes")] - public Minutes? Minutes { get; set; } - } - - public class KeepDisplayOn - { - public bool Value { get; set; } - } - - public class Hours - { - public int Value { get; set; } - } - - public class Minutes - { - public int Value { get; set; } - } - -} diff --git a/src/modules/espresso/Espresso/Program.cs b/src/modules/espresso/Espresso/Program.cs index acfeb0955b..1d0dc6695e 100644 --- a/src/modules/espresso/Espresso/Program.cs +++ b/src/modules/espresso/Espresso/Program.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using Espresso.Shell.Core; -using Espresso.Shell.Models; using ManagedCommon; +using Microsoft.PowerToys.Settings.UI.Library; using Newtonsoft.Json; using NLog; using System; @@ -19,7 +19,6 @@ using System.Reflection; using System.Threading; #pragma warning disable CS8602 // Dereference of a possibly null reference. -#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. #pragma warning disable CS8603 // Possible null reference return. namespace Espresso.Shell @@ -29,6 +28,7 @@ namespace Espresso.Shell private static Mutex? mutex = null; private const string appName = "Espresso"; private static FileSystemWatcher? watcher = null; + private static SettingsUtils? settingsUtils = null; public static Mutex Mutex { get => mutex; set => mutex = value; } @@ -45,7 +45,8 @@ namespace Espresso.Shell } log = LogManager.GetCurrentClassLogger(); - + settingsUtils = new SettingsUtils(); + log.Info("Launching Espresso..."); log.Info(FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion); log.Debug($"OS: {Environment.OSVersion}"); @@ -158,7 +159,7 @@ namespace Espresso.Shell // Initially the file might not be updated, so we need to start processing // settings right away. - ProcessSettings(config); + ProcessSettings(); } catch (Exception ex) { @@ -171,23 +172,12 @@ namespace Espresso.Shell { if (timeLimit <= 0) { - // Indefinite keep awake. - bool success = APIHelper.SetIndefiniteKeepAwake(displayOn); - if (success) - { - log.Info($"Currently in indefinite keep awake. Display always on: {displayOn}"); - } - else - { - var errorMessage = "Could not set up the state to be indefinite keep awake."; - log.Info(errorMessage); - log.Debug(errorMessage); - } + SetupIndefiniteKeepAwake(displayOn); } else { // Timed keep-awake. - APIHelper.SetTimedKeepAwake(timeLimit, LogTimedKeepAwakeCompletion, LogUnexpectedOrCancelledKeepAwakeCompletion, displayOn); + SetupTimedKeepAwake(timeLimit, displayOn); } } @@ -199,87 +189,78 @@ namespace Espresso.Shell }); } - TrayHelper.InitializeEspressoTray("Espresso", EspressoMode.INDEFINITE, true, new Action(()=>Console.WriteLine("test")), new Action(() => Console.WriteLine("test"))); +#pragma warning disable CS8604 // Possible null reference argument. + TrayHelper.InitializeTray(appName, APIHelper.Extract("shell32.dll", 21, true), null); +#pragma warning restore CS8604 // Possible null reference argument. new ManualResetEvent(false).WaitOne(); } + private static void SetupIndefiniteKeepAwake(bool displayOn) + { + // Indefinite keep awake. + bool success = APIHelper.SetIndefiniteKeepAwake(displayOn); + if (success) + { + log.Info($"Currently in indefinite keep awake. Display always on: {displayOn}"); + } + else + { + var errorMessage = "Could not set up the state to be indefinite keep awake."; + log.Info(errorMessage); + log.Debug(errorMessage); + } + } + private static void HandleEspressoConfigChange(FileSystemEventArgs fileEvent) { log.Info("Detected a settings file change. Updating configuration..."); log.Info("Resetting keep-awake to normal state due to settings change."); ResetNormalPowerState(); - ProcessSettings(fileEvent.FullPath); + ProcessSettings(); } - private static void ProcessSettings(string fullPath) + private static void ProcessSettings() { try { - EspressoSettingsModel settings = null; + EspressoSettings settings = settingsUtils.GetSettings(appName); - var fileStream = SettingsHelper.GetSettingsFile(fullPath, 3); - if (fileStream != null) + if (settings != null) { - using (fileStream) + // If the settings were successfully processed, we need to set the right mode of operation. + // INDEFINITE = 0 + // TIMED = 1 + switch (settings.Properties.Mode) { - using StreamReader reader = new StreamReader(fileStream); + case EspressoMode.INDEFINITE: + { + // Indefinite keep awake. + SetupIndefiniteKeepAwake(settings.Properties.KeepDisplayOn.Value); + break; + } + case EspressoMode.TIMED: + { + // Timed keep-awake. + long computedTime = (settings.Properties.Hours.Value * 60 * 60) + (settings.Properties.Minutes.Value * 60); + SetupTimedKeepAwake(computedTime, settings.Properties.KeepDisplayOn.Value); - settings = JsonConvert.DeserializeObject(reader.ReadToEnd()); + break; + } + default: + { + var errorMessage = "Unknown mode of operation. Check config file."; + log.Info(errorMessage); + log.Debug(errorMessage); + break; + } } - if (settings != null) - { - // If the settings were successfully processed, we need to set the right mode of operation. - // INDEFINITE = 0 - // TIMED = 1 - switch (settings.Properties.Mode) - { - case EspressoMode.INDEFINITE: - { - // Indefinite keep awake. - bool success = APIHelper.SetIndefiniteKeepAwake(settings.Properties.KeepDisplayOn.Value); - if (success) - { - log.Info($"Indefinite keep-awake. Display always on: {settings.Properties.KeepDisplayOn.Value}"); - } - else - { - var errorMessage = "Could not set up the state to be indefinite keep-awake."; - log.Info(errorMessage); - log.Debug(errorMessage); - } - break; - } - case EspressoMode.TIMED: - { - // Timed keep-awake. - long computedTime = (settings.Properties.Hours.Value * 60 * 60) + (settings.Properties.Minutes.Value * 60); - log.Info($"Timed keep-awake. Expected runtime: {computedTime} seconds."); - - APIHelper.SetTimedKeepAwake(computedTime, LogTimedKeepAwakeCompletion, LogUnexpectedOrCancelledKeepAwakeCompletion, settings.Properties.KeepDisplayOn.Value); - - break; - } - default: - { - var errorMessage= "Unknown mode of operation. Check config file."; - log.Info(errorMessage); - log.Debug(errorMessage); - break; - } - } - } - else - { - var errorMessage = "Settings are null."; - log.Info(errorMessage); - log.Debug(errorMessage); - } + TrayHelper.SetTray(appName, settings); } else { - var errorMessage = "Could not get handle on file."; + var errorMessage = "Settings are null."; log.Info(errorMessage); log.Debug(errorMessage); } @@ -289,10 +270,16 @@ namespace Espresso.Shell var errorMessage = $"There was a problem reading the configuration file. Error: {ex.Message}"; log.Info(errorMessage); log.Debug(errorMessage); - log.Debug($"Configuration path: {fullPath}"); } } + private static void SetupTimedKeepAwake(long time, bool displayOn) + { + log.Info($"Timed keep-awake. Expected runtime: {time} seconds."); + + APIHelper.SetTimedKeepAwake(time, LogTimedKeepAwakeCompletion, LogUnexpectedOrCancelledKeepAwakeCompletion, displayOn); + } + private static void LogUnexpectedOrCancelledKeepAwakeCompletion() { var errorMessage = "The keep-awake thread was terminated early.";