Change how settings and tray are handled.

This commit is contained in:
Den Delimarsky 2021-05-09 22:25:52 -07:00
parent 1347223707
commit af4ec9ad8b
No known key found for this signature in database
GPG key ID: E1BE1355085F0BCF
5 changed files with 152 additions and 184 deletions

View file

@ -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;
}
}
}

View file

@ -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<EspressoSettings>(text);
currentSettings.Properties.Mode = EspressoMode.INDEFINITE;
ModuleSettings.SaveSettings(JsonConvert.SerializeObject(currentSettings), text);
},
() => {
// Set timed keep awake.
var currentSettings = ModuleSettings.GetSettings<EspressoSettings>(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<EspressoSettings>(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;
}
}
}

View file

@ -49,6 +49,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\..\settings-ui\Microsoft.PowerToys.Settings.UI.Library\Microsoft.PowerToys.Settings.UI.Library.csproj" />
</ItemGroup>
<ItemGroup>

View file

@ -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; }
}
}

View file

@ -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<EspressoSettings>(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<EspressoSettingsModel>(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.";