Revert "Espresso" (#11235)

This commit is contained in:
Clint Rutkas
2021-05-12 11:22:34 -07:00
committed by GitHub
parent 803522ac7a
commit 48f2376aaa
45 changed files with 29 additions and 2242 deletions

View File

@@ -1,245 +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;
using System.Threading.Tasks;
using Microsoft.Win32;
using NLog;
namespace Espresso.Shell.Core
{
[Flags]
public enum EXECUTION_STATE : uint
{
ES_AWAYMODE_REQUIRED = 0x00000040,
ES_CONTINUOUS = 0x80000000,
ES_DISPLAY_REQUIRED = 0x00000002,
ES_SYSTEM_REQUIRED = 0x00000001,
}
/// <summary>
/// Helper class that allows talking to Win32 APIs without having to rely on PInvoke in other parts
/// of the codebase.
/// </summary>
public class APIHelper
{
private const string BuildRegistryLocation = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
public const int StdOutputHandle = -11;
public const int StdInputHandle = -10;
public const int StdErrorHandle = -12;
public const uint GenericWrite = 0x40000000;
public const uint GenericRead = 0x80000000;
private static readonly Logger _log;
private static CancellationTokenSource _tokenSource;
private static CancellationToken _threadToken;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile(
[MarshalAs(UnmanagedType.LPTStr)] string filename,
[MarshalAs(UnmanagedType.U4)] uint access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
IntPtr templateFile);
static APIHelper()
{
_log = LogManager.GetCurrentClassLogger();
_tokenSource = new CancellationTokenSource();
}
public static void AllocateConsole()
{
AllocConsole();
var outputFilePointer = CreateFile("CONOUT$", GenericRead | GenericWrite, FileShare.Write, IntPtr.Zero, FileMode.OpenOrCreate, 0, IntPtr.Zero);
SetStdHandle(StdOutputHandle, outputFilePointer);
Console.SetOut(new StreamWriter(Console.OpenStandardOutput(), Console.OutputEncoding) { AutoFlush = true });
}
/// <summary>
/// Sets the computer awake state using the native Win32 SetThreadExecutionState API. This
/// function is just a nice-to-have wrapper that helps avoid tracking the success or failure of
/// the call.
/// </summary>
/// <param name="state">Single or multiple EXECUTION_STATE entries.</param>
/// <returns>true if successful, false if failed</returns>
private static bool SetAwakeState(EXECUTION_STATE state)
{
try
{
var stateResult = SetThreadExecutionState(state);
return stateResult != 0;
}
catch
{
return false;
}
}
public static void SetIndefiniteKeepAwake(Action<bool> callback, Action failureCallback, bool keepDisplayOn = true)
{
_tokenSource = new CancellationTokenSource();
_threadToken = _tokenSource.Token;
Task.Run(() => RunIndefiniteLoop(keepDisplayOn), _threadToken)
.ContinueWith((result) => callback(result.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith((result) => failureCallback, TaskContinuationOptions.NotOnRanToCompletion);
}
public static void SetTimedKeepAwake(uint seconds, Action<bool> callback, Action failureCallback, bool keepDisplayOn = true)
{
_tokenSource = new CancellationTokenSource();
_threadToken = _tokenSource.Token;
Task.Run(() => RunTimedLoop(seconds, keepDisplayOn), _threadToken)
.ContinueWith((result) => callback(result.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith((result) => failureCallback, TaskContinuationOptions.NotOnRanToCompletion);
}
private static bool RunIndefiniteLoop(bool keepDisplayOn = true)
{
bool success;
if (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);
}
try
{
if (success)
{
_log.Info("Initiated indefinite keep awake in background thread.");
while (true)
{
if (_threadToken.IsCancellationRequested)
{
_threadToken.ThrowIfCancellationRequested();
}
}
}
else
{
_log.Info("Could not successfully set up indefinite keep awake.");
return success;
}
}
catch (OperationCanceledException ex)
{
// Task was clearly cancelled.
_log.Info($"Background thread termination. Message: {ex.Message}");
return success;
}
}
private static bool RunTimedLoop(uint seconds, bool keepDisplayOn = true)
{
bool success = false;
// In case cancellation was already requested.
_threadToken.ThrowIfCancellationRequested();
try
{
if (keepDisplayOn)
{
success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
if (success)
{
_log.Info("Timed keep-awake with display on.");
var startTime = DateTime.UtcNow;
while (DateTime.UtcNow - startTime < TimeSpan.FromSeconds(Math.Abs(seconds)))
{
if (_threadToken.IsCancellationRequested)
{
_threadToken.ThrowIfCancellationRequested();
}
}
return success;
}
else
{
_log.Info("Could not set up timed keep-awake with display on.");
return success;
}
}
else
{
success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
if (success)
{
_log.Info("Timed keep-awake with display off.");
var startTime = DateTime.UtcNow;
while (DateTime.UtcNow - startTime < TimeSpan.FromSeconds(Math.Abs(seconds)))
{
if (_threadToken.IsCancellationRequested)
{
_threadToken.ThrowIfCancellationRequested();
}
}
return success;
}
else
{
_log.Info("Could not set up timed keep-awake with display off.");
return success;
}
}
}
catch (OperationCanceledException ex)
{
// Task was clearly cancelled.
_log.Info($"Background thread termination. Message: {ex.Message}");
return success;
}
}
public static string GetOperatingSystemBuild()
{
try
{
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(BuildRegistryLocation);
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
if (registryKey != null)
{
var versionString = $"{registryKey.GetValue("ProductName")} {registryKey.GetValue("DisplayVersion")} {registryKey.GetValue("BuildLabEx")}";
return versionString;
}
else
{
_log.Debug("Registry key acquisition for OS failed.");
return string.Empty;
}
}
catch (Exception ex)
{
_log.Debug($"Could not get registry key for the build number. Error: {ex.Message}");
return string.Empty;
}
}
}
}

View File

@@ -1,226 +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.Drawing;
using System.Text.Json;
using System.Windows.Forms;
using Microsoft.PowerToys.Settings.UI.Library;
#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
{
private static NotifyIcon? trayIcon;
private static NotifyIcon TrayIcon { get => trayIcon; set => trayIcon = value; }
private static SettingsUtils? moduleSettings;
private static SettingsUtils ModuleSettings { get => moduleSettings; set => moduleSettings = value; }
static TrayHelper()
{
TrayIcon = new NotifyIcon();
ModuleSettings = new SettingsUtils();
}
public static void InitializeTray(string text, Icon icon, ContextMenuStrip? contextMenu = null)
{
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();
}, TrayIcon);
}
internal static void SetTray(string text, EspressoSettings settings)
{
SetTray(
text,
settings.Properties.KeepDisplayOn,
settings.Properties.Mode,
IndefiniteKeepAwakeCallback(text),
TimedKeepAwakeCallback(text),
KeepDisplayOnCallback(text),
ExitCallback());
}
private static Action ExitCallback()
{
return () =>
{
Environment.Exit(0);
};
}
private static Action KeepDisplayOnCallback(string moduleName)
{
return () =>
{
// Just changing the display mode.
var currentSettings = ModuleSettings.GetSettings<EspressoSettings>(moduleName);
currentSettings.Properties.KeepDisplayOn = !currentSettings.Properties.KeepDisplayOn;
ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
};
}
private static Action<uint, uint> TimedKeepAwakeCallback(string moduleName)
{
return (hours, minutes) =>
{
// Set timed keep awake.
var currentSettings = ModuleSettings.GetSettings<EspressoSettings>(moduleName);
currentSettings.Properties.Mode = EspressoMode.TIMED;
currentSettings.Properties.Hours = hours;
currentSettings.Properties.Minutes = minutes;
ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
};
}
private static Action IndefiniteKeepAwakeCallback(string moduleName)
{
return () =>
{
// Set indefinite keep awake.
var currentSettings = ModuleSettings.GetSettings<EspressoSettings>(moduleName);
currentSettings.Properties.Mode = EspressoMode.INDEFINITE;
ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
};
}
public static void SetTray(string text, bool keepDisplayOn, EspressoMode mode, Action indefiniteKeepAwakeCallback, Action<uint, uint> timedKeepAwakeCallback, Action keepDisplayOnCallback, Action exitCallback)
{
var contextMenuStrip = new ContextMenuStrip();
// Main toolstrip.
var operationContextMenu = new ToolStripMenuItem
{
Text = "Mode",
};
// Indefinite keep-awake menu item.
var indefiniteMenuItem = new ToolStripMenuItem
{
Text = "Keep awake indefinitely",
};
if (mode == EspressoMode.INDEFINITE)
{
indefiniteMenuItem.Checked = true;
}
else
{
indefiniteMenuItem.Checked = false;
}
indefiniteMenuItem.Click += (e, s) =>
{
// User opted to set the mode to indefinite, so we need to write new settings.
indefiniteKeepAwakeCallback();
};
var displayOnMenuItem = new ToolStripMenuItem
{
Text = "Keep display on",
};
if (keepDisplayOn)
{
displayOnMenuItem.Checked = true;
}
else
{
displayOnMenuItem.Checked = false;
}
displayOnMenuItem.Click += (e, s) =>
{
// User opted to set the display mode directly.
keepDisplayOnCallback();
};
// Timed keep-awake menu item
var timedMenuItem = new ToolStripMenuItem
{
Text = "Keep awake temporarily",
};
if (mode == EspressoMode.TIMED)
{
timedMenuItem.Checked = true;
}
else
{
timedMenuItem.Checked = false;
}
var 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
{
Text = "1 hour",
};
oneHourMenuItem.Click += (e, s) =>
{
// User is setting the keep-awake to 1 hour.
timedKeepAwakeCallback(1, 0);
};
var twoHoursMenuItem = new ToolStripMenuItem
{
Text = "2 hours",
};
twoHoursMenuItem.Click += (e, s) =>
{
// User is setting the keep-awake to 2 hours.
timedKeepAwakeCallback(2, 0);
};
// Exit menu item.
var exitContextMenu = new ToolStripMenuItem
{
Text = "Exit",
};
exitContextMenu.Click += (e, s) =>
{
// User is setting the keep-awake to 2 hours.
exitCallback();
};
timedMenuItem.DropDownItems.Add(halfHourMenuItem);
timedMenuItem.DropDownItems.Add(oneHourMenuItem);
timedMenuItem.DropDownItems.Add(twoHoursMenuItem);
operationContextMenu.DropDownItems.Add(indefiniteMenuItem);
operationContextMenu.DropDownItems.Add(timedMenuItem);
operationContextMenu.DropDownItems.Add(new ToolStripSeparator());
operationContextMenu.DropDownItems.Add(displayOnMenuItem);
contextMenuStrip.Items.Add(operationContextMenu);
contextMenuStrip.Items.Add(new ToolStripSeparator());
contextMenuStrip.Items.Add(exitContextMenu);
TrayIcon.Text = text;
TrayIcon.ContextMenuStrip = contextMenuStrip;
}
}
}

View File

@@ -1,87 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\Version.props" />
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\Espresso</OutputPath>
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<UseWindowsForms>true</UseWindowsForms>
<!--Per documentation: https://docs.microsoft.com/dotnet/core/compatibility/windows-forms/5.0/automatically-infer-winexe-output-type#outputtype-set-to-winexe-for-wpf-and-winforms-apps -->
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
<AssemblyName>PowerToys.Espresso</AssemblyName>
<Version>$(Version).0</Version>
<ApplicationIcon>Images\Espresso.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<Optimize>false</Optimize>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DefineConstants>TRACE;RELEASE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<None Remove="Images\Espresso.ico" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="NLog" Version="4.7.9" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20071.2" />
<PackageReference Include="System.Reactive" Version="5.0.0" />
<PackageReference Include="System.Runtime.Caching" Version="6.0.0-preview.1.21102.12" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\..\settings-ui\Microsoft.PowerToys.Settings.UI.Library\Microsoft.PowerToys.Settings.UI.Library.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Program.cs">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="NLog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\codeAnalysis\GlobalSuppressions.cs">
<Link>GlobalSuppressions.cs</Link>
</Compile>
<AdditionalFiles Include="..\..\..\codeAnalysis\StyleCop.json">
<Link>StyleCop.json</Link>
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers">
<Version>1.1.118</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Resource Include="Images\Espresso.ico" />
</ItemGroup>
</Project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets async="true">
<target name="logfile" xsi:type="File" fileName="espresso.log" layout="[${longdate} ${level:uppercase=true} ${logger}] ${message}" />
<target name="logconsole" xsi:type="Console" layout="[${longdate} ${level:uppercase=true}] ${message}" />
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="logconsole" />
<logger name="*" minlevel="Debug" writeTo="logfile" />
</rules>
</nlog>

View File

@@ -1,294 +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.CommandLine;
using System.CommandLine.Invocation;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reflection;
using System.Threading;
using System.Windows;
using Espresso.Shell.Core;
using ManagedCommon;
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.
namespace Espresso.Shell
{
internal class Program
{
private static Mutex? _mutex = null;
private const string AppName = "Espresso";
private static FileSystemWatcher? _watcher = null;
private static SettingsUtils? _settingsUtils = null;
public static Mutex LockMutex { get => _mutex; set => _mutex = value; }
private static Logger? _log;
private static int Main(string[] args)
{
bool instantiated;
LockMutex = new Mutex(true, AppName, out instantiated);
if (!instantiated)
{
ForceExit(AppName + " is already running! Exiting the application.", 1);
}
_log = LogManager.GetCurrentClassLogger();
_settingsUtils = new SettingsUtils();
_log.Info("Launching Espresso...");
_log.Info(FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion);
_log.Debug($"OS: {Environment.OSVersion}");
_log.Debug($"OS Build: {APIHelper.GetOperatingSystemBuild()}");
var configOption = new Option<bool>(
aliases: new[] { "--use-pt-config", "-c" },
getDefaultValue: () => false,
description: "Specifies whether Espresso will be using the PowerToys configuration file for managing the state.")
{
Argument = new Argument<bool>(() => false)
{
Arity = ArgumentArity.ZeroOrOne,
},
};
configOption.Required = false;
var displayOption = new Option<bool>(
aliases: new[] { "--display-on", "-d" },
getDefaultValue: () => true,
description: "Determines whether the display should be kept awake.")
{
Argument = new Argument<bool>(() => false)
{
Arity = ArgumentArity.ZeroOrOne,
},
};
displayOption.Required = false;
var timeOption = new Option<uint>(
aliases: new[] { "--time-limit", "-t" },
getDefaultValue: () => 0,
description: "Determines the interval, in seconds, during which the computer is kept awake.")
{
Argument = new Argument<uint>(() => 0)
{
Arity = ArgumentArity.ExactlyOne,
},
};
timeOption.Required = false;
var pidOption = new Option<int>(
aliases: new[] { "--pid", "-p" },
getDefaultValue: () => 0,
description: "Bind the execution of Espresso to another process.")
{
Argument = new Argument<int>(() => 0)
{
Arity = ArgumentArity.ZeroOrOne,
},
};
pidOption.Required = false;
var rootCommand = new RootCommand
{
configOption,
displayOption,
timeOption,
pidOption,
};
rootCommand.Description = AppName;
rootCommand.Handler = CommandHandler.Create<bool, bool, uint, int>(HandleCommandLineArguments);
return rootCommand.InvokeAsync(args).Result;
}
private static void ForceExit(string message, int exitCode)
{
_log.Debug(message);
_log.Info(message);
Console.ReadKey();
Environment.Exit(exitCode);
}
private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, uint timeLimit, int pid)
{
if (pid == 0)
{
APIHelper.AllocateConsole();
}
_log.Info($"The value for --use-pt-config is: {usePtConfig}");
_log.Info($"The value for --display-on is: {displayOn}");
_log.Info($"The value for --time-limit is: {timeLimit}");
_log.Info($"The value for --pid is: {pid}");
if (usePtConfig)
{
#pragma warning disable CS8604 // Possible null reference argument.
TrayHelper.InitializeTray(AppName, new Icon(Application.GetResourceStream(new Uri("/Images/Espresso.ico", UriKind.Relative)).Stream));
#pragma warning restore CS8604 // Possible null reference argument.
// Configuration file is used, therefore we disregard any other command-line parameter
// and instead watch for changes in the file.
try
{
var settingsPath = _settingsUtils.GetSettingsFilePath(AppName);
_log.Info($"Reading configuration file: {settingsPath}");
_watcher = new FileSystemWatcher
{
Path = Path.GetDirectoryName(settingsPath),
EnableRaisingEvents = true,
NotifyFilter = NotifyFilters.LastWrite,
Filter = Path.GetFileName(settingsPath),
};
Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
h => _watcher.Changed += h,
h => _watcher.Changed -= h)
.SubscribeOn(TaskPoolScheduler.Default)
.Select(e => e.EventArgs)
.Throttle(TimeSpan.FromMilliseconds(25))
.Subscribe(HandleEspressoConfigChange);
// Initially the file might not be updated, so we need to start processing
// settings right away.
ProcessSettings();
}
catch (Exception ex)
{
var 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 ? EspressoMode.INDEFINITE : EspressoMode.TIMED;
if (mode == EspressoMode.INDEFINITE)
{
SetupIndefiniteKeepAwake(displayOn);
}
else
{
SetupTimedKeepAwake(timeLimit, displayOn);
}
}
var exitSignal = new ManualResetEvent(false);
if (pid != 0)
{
RunnerHelper.WaitForPowerToysRunner(pid, () =>
{
exitSignal.Set();
Environment.Exit(0);
});
}
exitSignal.WaitOne();
}
private static void SetupIndefiniteKeepAwake(bool displayOn)
{
// Indefinite keep awake.
APIHelper.SetIndefiniteKeepAwake(LogCompletedKeepAwakeThread, LogUnexpectedOrCancelledKeepAwakeThreadCompletion, displayOn);
}
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.");
ProcessSettings();
}
private static void ProcessSettings()
{
try
{
EspressoSettings settings = _settingsUtils.GetSettings<EspressoSettings>(AppName);
if (settings != null)
{
switch (settings.Properties.Mode)
{
case EspressoMode.INDEFINITE:
{
// Indefinite keep awake.
SetupIndefiniteKeepAwake(settings.Properties.KeepDisplayOn);
break;
}
case EspressoMode.TIMED:
{
// Timed keep-awake.
uint computedTime = (settings.Properties.Hours * 60 * 60) + (settings.Properties.Minutes * 60);
SetupTimedKeepAwake(computedTime, settings.Properties.KeepDisplayOn);
break;
}
default:
{
var errorMessage = "Unknown mode of operation. Check config file.";
_log.Info(errorMessage);
_log.Debug(errorMessage);
break;
}
}
TrayHelper.SetTray(AppName, settings);
}
else
{
var 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.Message}";
_log.Info(errorMessage);
_log.Debug(errorMessage);
}
}
private static void SetupTimedKeepAwake(uint time, bool displayOn)
{
_log.Info($"Timed keep-awake. Expected runtime: {time} seconds with display on setting set to {displayOn}.");
APIHelper.SetTimedKeepAwake(time, LogCompletedKeepAwakeThread, LogUnexpectedOrCancelledKeepAwakeThreadCompletion, displayOn);
}
private static void LogUnexpectedOrCancelledKeepAwakeThreadCompletion()
{
var errorMessage = "The keep-awake thread was terminated early.";
_log.Info(errorMessage);
_log.Debug(errorMessage);
}
private static void LogCompletedKeepAwakeThread(bool result)
{
_log.Info($"Exited keep-awake thread successfully: {result}");
}
}
}

View File

@@ -1,7 +0,0 @@
#include <string>
namespace EspressoConstants
{
// Name of the powertoy module.
inline const std::wstring ModuleKey = L"Espresso";
}

View File

@@ -1,72 +0,0 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE
BEGIN
IDS_ESPRESSO_NAME "Espresso"
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@@ -1,92 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{5e7360a8-d048-4ed3-8f09-0bfd64c5529a}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>Espresso</RootNamespace>
<ProjectName>EspressoModuleInterface</ProjectName>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\Espresso\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="EspressoConstants.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="trace.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="trace.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SetttingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="EspressoModuleInterface.rc" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View File

@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="trace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="EspressoConstants.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="trace.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Generated Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Header Files">
<UniqueIdentifier>{e8ef1c4e-cc50-4ce5-b00d-4e3ac5c1a7db}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{fbd9cdd2-e7d5-4417-9b52-25e345ae9562}</UniqueIdentifier>
</Filter>
<Filter Include="Generated Files">
<UniqueIdentifier>{c2a23a2b-5846-440f-b29e-eea748dba12d}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{77f1702b-da7f-4ff6-90a3-19db515cf963}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="EspressoModuleInterface.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@@ -1,226 +0,0 @@
#include "pch.h"
#include <interface/powertoy_module_interface.h>
#include <common/SettingsAPI/settings_objects.h>
#include <common/interop/shared_constants.h>
#include "trace.h"
#include "resource.h"
#include "EspressoConstants.h"
#include <common/logger/logger.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/utils/elevation.h>
#include <common/utils/process_path.h>
#include <common/utils/resources.h>
#include <common/utils/os-detect.h>
#include <common/utils/winapi_error.h>
#include <filesystem>
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Trace::RegisterProvider();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
Trace::UnregisterProvider();
break;
}
return TRUE;
}
// The PowerToy name that will be shown in the settings.
const static wchar_t* MODULE_NAME = L"Espresso";
// Add a description that will we shown in the module settings page.
const static wchar_t* MODULE_DESC = L"<no description>";
// These are the properties shown in the Settings page.
struct ModuleSettings
{
} g_settings;
// Implement the PowerToy Module Interface and all the required methods.
class Espresso : 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 Espresso
HANDLE m_hInvokeEvent;
// Load initial settings from the persisted values.
void init_settings();
bool is_process_running()
{
return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT;
}
void launch_process()
{
Logger::trace(L"Launching Espresso process");
unsigned long powertoys_pid = GetCurrentProcessId();
std::wstring executable_args = L"--use-pt-config --pid " + std::to_wstring(powertoys_pid);
Logger::trace(L"Espresso launching with parameters: " + executable_args);
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = L"modules\\Espresso\\PowerToys.Espresso.exe";
sei.nShow = SW_SHOWNORMAL;
sei.lpParameters = executable_args.data();
if (!ShellExecuteExW(&sei))
{
DWORD error = GetLastError();
std::wstring message = L"Espresso failed to start with error = ";
message += std::to_wstring(error);
Logger::error(message);
}
m_hProcess = sei.hProcess;
}
public:
// Constructor
Espresso()
{
app_name = GET_RESOURCE_STRING(IDS_ESPRESSO_NAME);
app_key = EspressoConstants::ModuleKey;
std::filesystem::path logFilePath(PTSettingsHelper::get_module_save_folder_location(this->app_key));
Logger::init(LogSettings::launcherLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
Logger::info("Launcher object is constructing");
init_settings();
};
// 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();
}
// Signal from the Settings editor to call a custom action.
// This can be used to spawn more complex editors.
virtual void call_custom_action(const wchar_t* action) override
{
static UINT custom_action_num_calls = 0;
try
{
// Parse the action values, including name.
PowerToysSettings::CustomActionObject action_object =
PowerToysSettings::CustomActionObject::from_json_string(action);
}
catch (std::exception&)
{
// Improper JSON.
}
}
// Called by the runner to pass the updated settings values as a serialized JSON.
virtual void set_config(const wchar_t* config) override
{
try
{
// Parse the input JSON string.
PowerToysSettings::PowerToyValues values =
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:
values.save_to_settings_file();
}
catch (std::exception&)
{
// Improper JSON.
}
}
virtual void enable()
{
ResetEvent(send_telemetry_event);
ResetEvent(m_hInvokeEvent);
launch_process();
m_enabled = true;
};
virtual void disable()
{
if (m_enabled)
{
ResetEvent(send_telemetry_event);
ResetEvent(m_hInvokeEvent);
TerminateProcess(m_hProcess, 1);
}
m_enabled = false;
}
// Returns if the powertoys is enabled
virtual bool is_enabled() override
{
return m_enabled;
}
};
// Load the settings file.
void Espresso::init_settings()
{
try
{
// Load and parse the settings file for this PowerToy.
PowerToysSettings::PowerToyValues settings =
PowerToysSettings::PowerToyValues::load_from_settings_file(get_key());
}
catch (std::exception ex)
{
Logger::warn(L"An exception occurred while loading the settings file");
// Error while loading from the settings file. Let default values stay as they are.
}
}
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
return new Espresso();
}

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
</packages>

View File

@@ -1 +0,0 @@
#include "pch.h"

View File

@@ -1,7 +0,0 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <ProjectTelemetry.h>
#include <shellapi.h>
#include <Shlwapi.h>

View File

@@ -1,16 +0,0 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Espresso.rc
//
#define IDS_ESPRESSO_NAME 101
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@@ -1,19 +0,0 @@
#include "pch.h"
#include "trace.h"
TRACELOGGING_DEFINE_PROVIDER(
g_hProvider,
"Microsoft.PowerToys",
// {38e8889b-9731-53f5-e901-e8a7c1753074}
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
TraceLoggingOptionProjectTelemetry());
void Trace::RegisterProvider()
{
TraceLoggingRegister(g_hProvider);
}
void Trace::UnregisterProvider()
{
TraceLoggingUnregister(g_hProvider);
}

View File

@@ -1,8 +0,0 @@
#pragma once
class Trace
{
public:
static void RegisterProvider();
static void UnregisterProvider();
};