Compare commits

...

5 Commits

Author SHA1 Message Date
Andrey Nekrasov
18fc38115b Don't use Environment.Exit (#20532)
* [Awake] Don't use Process.Exit and move to CsWin32

* [PowerLauncher] Remove unused API

* [ColorPicker] Use cancellable NativeEventWaiter + cleanup using

* [TextExtractor] Don't use Environment.Exit

* [MeasureTool] Don't use Environment.Exit(0);

* [FZE] don't use Environment.Exit and fix WaitForPowerToysRunner
2022-09-13 17:27:56 +01:00
Andrey Nekrasov
9d8bfead0c [PTRun] Gracefully shutdown all threads when exiting (#20450) 2022-09-13 17:27:45 +01:00
Andrey Nekrasov
8872cf651b [Installer] Gracefully terminate PowerToys process before updating do… (#20430)
* [Installer] Gracefully terminate PowerToys process before updating dotnet
2022-09-13 17:25:37 +01:00
Stefan Markovic
702b69d5a4 [PowerAccent] Move low-level keyboard hook to c++ (#20190)
* Move llkeyboardhook to c++

* expect.txt

* Address PR comment - Resolve namespaces

* Address PR comments - CallNextHook and correct char selection

* Also unpress the letter key

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
2022-09-13 17:25:07 +01:00
Andrey Nekrasov
bdc1889eba [BugReportTool] Make failures to copying LocalRow logs and deleting tmp folder non-fatal (#20311) 2022-09-13 17:24:42 +01:00
63 changed files with 1064 additions and 1050 deletions

View File

@@ -60,7 +60,6 @@ APeriod
api
APIENTRY
APIIs
Apm
APPBARDATA
appdata
APPICON
@@ -112,7 +111,6 @@ atlstr
Attribs
attrs
Atyrau
augustkarlstedt
aumid
Aut
Authenticode
@@ -131,7 +129,6 @@ Avanc
Awaitable
awakeness
awakeversion
AWAYMODE
AYUV
backend
backtracer
@@ -144,7 +141,6 @@ bck
Belarus
betadele
betsegaw
bezgumption
BGR
bgra
bhid
@@ -500,7 +496,6 @@ dvr
DVSD
DVSL
DVTARGETDEVICE
dwhkl
DWINRT
dwl
dwm
@@ -540,6 +535,7 @@ eltociear
emmintrin
Emoji
emptyrecyclebin
ENABLEDELAYEDEXPANSION
ENABLEDPOPUP
endforeach
endian
@@ -560,6 +556,7 @@ ERASEBKGND
EREOF
EResize
ERole
ERRORLEVEL
ERRORMESSAGE
ERRORTITLE
ESettings
@@ -727,8 +724,6 @@ hhk
HHmmss
HHOOK
hhx
Hiber
Hiberboot
HIBYTE
HICON
HIDEWINDOW
@@ -742,7 +737,7 @@ HKCC
HKCR
HKCU
hkey
hkl
HKL
HKLM
HKPD
HKU
@@ -904,7 +899,6 @@ inheritdoc
initguid
Inkscape
Inlines
Inlining
inorder
INotification
INotify
@@ -1034,7 +1028,6 @@ jxr
jyuwono
KBDLLHOOKSTRUCT
kbm
KCode
KEYBDINPUT
keybindings
keyboardeventhandlers
@@ -1158,7 +1151,6 @@ lpsz
lpt
LPTHREAD
LPTOP
lptpm
LPTSTR
LPVOID
LPW
@@ -1223,7 +1215,6 @@ Melman
memcmp
memcpy
memset
MENUBREAK
MENUITEMINFO
MENUITEMINFOW
Metadatas
@@ -1352,6 +1343,7 @@ NDEBUG
Ndombe
ndp
NEEDDISPATCH
NEQ
NESW
netcore
netcoreapp
@@ -1427,7 +1419,6 @@ ntdll
NTFS
NTSTATUS
nuget
nuint
nullonfailure
nullopt
nullptr
@@ -1470,7 +1461,6 @@ openxmlformats
OPTIMIZEFORINVOKE
ORAW
ORPHANEDDIALOGTITLE
osfanbuff
oss
ostr
ostream
@@ -1490,7 +1480,6 @@ overlaywindow
Overridable
Oversampling
OWNDC
OWNERDRAW
PACL
pagos
PAINTSTRUCT
@@ -1532,6 +1521,7 @@ pfo
pft
pgp
pguid
PHANDLER
phbm
phbmp
phwnd
@@ -1752,7 +1742,6 @@ RNumber
roadmap
robmensching
Roboto
rohanrdy
rooler
roslyn
Rothera
@@ -1935,6 +1924,7 @@ sse
ssf
ssh
sstream
stackalloc
STACKFRAME
stackoverflow
stackpanel
@@ -2231,14 +2221,12 @@ VIDEOINFOHEADER
viewbox
viewmodel
vih
Virt
virtualization
Virtualizing
visiblecolorformats
Visibletrue
visualbrush
visualstudio
viter
VKey
VKTAB
vmovl

View File

@@ -117,6 +117,7 @@
"modules\\PowerAccent\\PowerToys.PowerAccent.dll",
"modules\\PowerAccent\\PowerToys.PowerAccent.exe",
"modules\\PowerAccent\\PowerToys.PowerAccentModuleInterface.dll",
"modules\\PowerAccent\\PowerToys.PowerAccentKeyboardService.dll",
"modules\\PowerRename\\PowerToys.PowerRenameExt.dll",
"modules\\PowerRename\\PowerToys.PowerRename.exe",

View File

@@ -447,6 +447,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MeasureToolModuleInterface"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MeasureToolUI", "src\modules\MeasureTool\MeasureToolUI\MeasureToolUI.csproj", "{515554D1-D004-4F7F-A107-2211FC0F6B2C}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerAccentKeyboardService", "src\modules\poweraccent\PowerAccentKeyboardService\PowerAccentKeyboardService.vcxproj", "{C97D9A5D-206C-454E-997E-009E227D7F02}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -1790,6 +1792,18 @@ Global
{515554D1-D004-4F7F-A107-2211FC0F6B2C}.Release|x86.ActiveCfg = Release|x86
{515554D1-D004-4F7F-A107-2211FC0F6B2C}.Release|x86.Build.0 = Release|x86
{515554D1-D004-4F7F-A107-2211FC0F6B2C}.Release|x86.Deploy.0 = Release|x86
{C97D9A5D-206C-454E-997E-009E227D7F02}.Debug|ARM64.ActiveCfg = Debug|ARM64
{C97D9A5D-206C-454E-997E-009E227D7F02}.Debug|ARM64.Build.0 = Debug|ARM64
{C97D9A5D-206C-454E-997E-009E227D7F02}.Debug|x64.ActiveCfg = Debug|x64
{C97D9A5D-206C-454E-997E-009E227D7F02}.Debug|x64.Build.0 = Debug|x64
{C97D9A5D-206C-454E-997E-009E227D7F02}.Debug|x86.ActiveCfg = Debug|x64
{C97D9A5D-206C-454E-997E-009E227D7F02}.Debug|x86.Build.0 = Debug|x64
{C97D9A5D-206C-454E-997E-009E227D7F02}.Release|ARM64.ActiveCfg = Release|ARM64
{C97D9A5D-206C-454E-997E-009E227D7F02}.Release|ARM64.Build.0 = Release|ARM64
{C97D9A5D-206C-454E-997E-009E227D7F02}.Release|x64.ActiveCfg = Release|x64
{C97D9A5D-206C-454E-997E-009E227D7F02}.Release|x64.Build.0 = Release|x64
{C97D9A5D-206C-454E-997E-009E227D7F02}.Release|x86.ActiveCfg = Release|x64
{C97D9A5D-206C-454E-997E-009E227D7F02}.Release|x86.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1941,6 +1955,7 @@ Global
{54A93AF7-60C7-4F6C-99D2-FBB1F75F853A} = {7AC943C9-52E8-44CF-9083-744D8049667B}
{92C39820-9F84-4529-BC7D-22AAE514D63B} = {7AC943C9-52E8-44CF-9083-744D8049667B}
{515554D1-D004-4F7F-A107-2211FC0F6B2C} = {7AC943C9-52E8-44CF-9083-744D8049667B}
{C97D9A5D-206C-454E-997E-009E227D7F02} = {0F14491C-6369-4C45-AAA8-135814E66E6B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

View File

@@ -72,6 +72,16 @@
<util:RegistrySearch Variable="DetectedVCRedistVersion" Root="HKLM" Key="Software\Microsoft\VisualStudio\14.0\VC\Runtimes\$(var.PowerToysPlatform)" Value="Minor" Result="value" Format="raw" />
<Chain>
<ExePackage
Name="terminate_powertoys.cmd"
Cache="no"
Compressed="yes"
Id="TerminatePowerToys"
SourceFile="terminate_powertoys.cmd"
Permanent="yes"
PerMachine="yes"
Vital="no">
</ExePackage>
<ExePackage
Name="windowsdesktop-runtime-6.0.8-win-$(var.PowerToysPlatform).exe"
Compressed="no"

View File

@@ -125,7 +125,7 @@
<?define MeasureToolMicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?>
<?define PowerAccentFiles=ControlzEx.dll;GongSolutions.WPF.DragDrop.dll;Ijwhost.dll;MahApps.Metro.dll;Microsoft.Xaml.Behaviors.dll;PowerAccent.Core.dll;PowerAccent.deps.json;PowerAccent.dll;PowerAccent.exe;PowerAccent.runtimeconfig.json;PowerToys.PowerAccentModuleInterface.dll;PowerToys.Interop.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.PowerAccent.deps.json;PowerToys.PowerAccent.dll;PowerToys.PowerAccent.exe;PowerToys.PowerAccent.runtimeconfig.json;PowerToys.Settings.UI.Lib.dll;System.IO.Abstractions.dll;System.Management.dll;System.Text.Json.dll;Vanara.Core.dll;Vanara.PInvoke.Gdi32.dll;Vanara.PInvoke.Kernel32.dll;Vanara.PInvoke.Shared.dll;Vanara.PInvoke.User32.dll?>
<?define PowerAccentFiles=ControlzEx.dll;GongSolutions.WPF.DragDrop.dll;Ijwhost.dll;MahApps.Metro.dll;Microsoft.Xaml.Behaviors.dll;PowerAccent.Core.dll;PowerAccent.deps.json;PowerAccent.dll;PowerAccent.exe;PowerAccent.runtimeconfig.json;PowerToys.PowerAccentModuleInterface.dll;PowerToys.Interop.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.PowerAccent.deps.json;PowerToys.PowerAccent.dll;PowerToys.PowerAccent.exe;PowerToys.PowerAccent.runtimeconfig.json;PowerToys.Settings.UI.Lib.dll;System.IO.Abstractions.dll;System.Management.dll;System.Text.Json.dll;Vanara.Core.dll;Vanara.PInvoke.Gdi32.dll;Vanara.PInvoke.Kernel32.dll;Vanara.PInvoke.Shared.dll;Vanara.PInvoke.User32.dll;PowerToys.PowerAccentKeyboardService.dll;Microsoft.Windows.SDK.NET.dll;WinRT.Runtime.dll?>
<Product Id="*"
Name="PowerToys (Preview)"

View File

@@ -0,0 +1,12 @@
@echo off
setlocal ENABLEDELAYEDEXPANSION
@REM We loop here until taskkill cannot find a PowerToys process. We can't use /F flag, because it
@REM doesn't give application an opportunity to cleanup. Thus we send WM_CLOSE which is being caught
@REM by multiple windows running a msg loop in PowerToys.exe process, which we close one by one.
for /l %%x in (1, 1, 100) do (
taskkill /IM PowerToys.exe 1>NUL 2>NUL
if !ERRORLEVEL! NEQ 0 goto quit
)
:quit

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using ControlzEx.Theming;
namespace Common.UI

View File

@@ -4,23 +4,27 @@
using System;
using System.Threading;
using System.Windows;
namespace PowerOCR.Helpers
using Dispatcher = System.Windows.Threading.Dispatcher;
namespace Common.UI
{
public static class NativeEventWaiter
{
public static void WaitForEventLoop(string eventName, Action callback)
public static void WaitForEventLoop(string eventName, Action callback, Dispatcher dispatcher, CancellationToken cancel)
{
new Thread(() =>
{
var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
var eventHandle = new EventWaitHandle(false, EventResetMode.ManualReset, eventName);
while (true)
{
if (eventHandle.WaitOne())
if (WaitHandle.WaitAny(new WaitHandle[] { cancel.WaitHandle, eventHandle }) == 1)
{
Logger.LogInfo($"Successfully waited for {eventName}");
Application.Current.Dispatcher.Invoke(callback);
dispatcher.BeginInvoke(callback);
}
else
{
return;
}
}
}).Start();

View File

@@ -5,6 +5,7 @@
using System;
using ManagedCommon;
using MeasureToolUI.Helpers;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
namespace MeasureToolUI
@@ -36,9 +37,10 @@ namespace MeasureToolUI
{
if (int.TryParse(cmdArgs[cmdArgs.Length - 1], out int powerToysRunnerPid))
{
var dispatcher = DispatcherQueue.GetForCurrentThread();
RunnerHelper.WaitForPowerToysRunner(powerToysRunnerPid, () =>
{
Environment.Exit(0);
dispatcher.TryEnqueue(App.Current.Exit);
});
}
}
@@ -51,7 +53,8 @@ namespace MeasureToolUI
catch (Exception ex)
{
Logger.LogError($"MeasureToolCore failed to initialize: {ex}");
Environment.Exit(1);
App.Current.Exit();
return;
}
_window = new MainWindow(core);

View File

@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using ManagedCommon;
@@ -23,6 +22,13 @@ public partial class App : Application, IDisposable
private Mutex? _instanceMutex;
private int _powerToysRunnerPid;
private CancellationTokenSource NativeThreadCTS { get; set; }
public App()
{
NativeThreadCTS = new CancellationTokenSource();
}
public void Dispose()
{
GC.SuppressFinalize(this);
@@ -37,7 +43,7 @@ public partial class App : Application, IDisposable
{
Logger.LogWarning("Another running TextExtractor instance was detected. Exiting TextExtractor");
_instanceMutex = null;
Environment.Exit(0);
Shutdown();
return;
}
@@ -51,10 +57,11 @@ public partial class App : Application, IDisposable
RunnerHelper.WaitForPowerToysRunner(_powerToysRunnerPid, () =>
{
Logger.LogInfo("PowerToys Runner exited. Exiting TextExtractor");
Environment.Exit(0);
NativeThreadCTS.Cancel();
Application.Current.Dispatcher.Invoke(() => Shutdown());
});
var userSettings = new UserSettings(new Helpers.ThrottledActionInvoker());
eventMonitor = new EventMonitor();
eventMonitor = new EventMonitor(Application.Current.Dispatcher, NativeThreadCTS.Token);
}
catch (Exception ex)
{

View File

@@ -4,6 +4,7 @@
using System;
using System.Windows.Interop;
using Common.UI;
using interop;
using PowerOCR.Helpers;
using PowerOCR.Utilities;
@@ -16,9 +17,9 @@ namespace PowerOCR.Keyboard
/// </summary>
internal class EventMonitor
{
public EventMonitor()
public EventMonitor(System.Windows.Threading.Dispatcher dispatcher, System.Threading.CancellationToken exitToken)
{
NativeEventWaiter.WaitForEventLoop(Constants.ShowPowerOCRSharedEvent(), StartOCRSession);
NativeEventWaiter.WaitForEventLoop(Constants.ShowPowerOCRSharedEvent(), StartOCRSession, dispatcher, exitToken);
}
public void StartOCRSession()

View File

@@ -43,6 +43,9 @@
<ItemGroup>
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="NLog" Version="4.7.13" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20071.2" />
<PackageReference Include="System.Reactive" Version="5.0.0" />

View File

@@ -7,17 +7,20 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Awake.Core.Models;
using Microsoft.Win32;
using NLog;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Storage.FileSystem;
using Windows.Win32.System.Console;
using Windows.Win32.System.Power;
namespace Awake.Core
{
public delegate bool ConsoleEventHandler(ControlType ctrlType);
/// <summary>
/// Helper class that allows talking to Win32 APIs without having to rely on PInvoke in other parts
/// of the codebase.
@@ -25,9 +28,6 @@ namespace Awake.Core
public class APIHelper
{
private const string BuildRegistryLocation = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
private const int StdOutputHandle = -11;
private const uint GenericWrite = 0x40000000;
private const uint GenericRead = 0x80000000;
private static readonly Logger _log;
private static CancellationTokenSource _tokenSource;
@@ -43,21 +43,21 @@ namespace Awake.Core
_tokenSource = new CancellationTokenSource();
}
public static void SetConsoleControlHandler(ConsoleEventHandler handler, bool addHandler)
internal static void SetConsoleControlHandler(PHANDLER_ROUTINE handler, bool addHandler)
{
NativeMethods.SetConsoleCtrlHandler(handler, addHandler);
PInvoke.SetConsoleCtrlHandler(handler, addHandler);
}
public static void AllocateConsole()
{
_log.Debug("Bootstrapping the console allocation routine.");
NativeMethods.AllocConsole();
PInvoke.AllocConsole();
_log.Debug($"Console allocation result: {Marshal.GetLastWin32Error()}");
var outputFilePointer = NativeMethods.CreateFile("CONOUT$", GenericRead | GenericWrite, FileShare.Write, IntPtr.Zero, FileMode.OpenOrCreate, 0, IntPtr.Zero);
var outputFilePointer = PInvoke.CreateFile("CONOUT$", FILE_ACCESS_FLAGS.FILE_GENERIC_READ | FILE_ACCESS_FLAGS.FILE_GENERIC_WRITE, FILE_SHARE_MODE.FILE_SHARE_WRITE, null, FILE_CREATION_DISPOSITION.OPEN_EXISTING, 0, null);
_log.Debug($"CONOUT creation result: {Marshal.GetLastWin32Error()}");
NativeMethods.SetStdHandle(StdOutputHandle, outputFilePointer);
PInvoke.SetStdHandle(Windows.Win32.System.Console.STD_HANDLE.STD_OUTPUT_HANDLE, outputFilePointer);
_log.Debug($"SetStdHandle result: {Marshal.GetLastWin32Error()}");
Console.SetOut(new StreamWriter(Console.OpenStandardOutput(), Console.OutputEncoding) { AutoFlush = true });
@@ -70,11 +70,11 @@ namespace Awake.Core
/// </summary>
/// <param name="state">Single or multiple EXECUTION_STATE entries.</param>
/// <returns>true if successful, false if failed</returns>
private static bool SetAwakeState(ExecutionState state)
private static bool SetAwakeState(EXECUTION_STATE state)
{
try
{
var stateResult = NativeMethods.SetThreadExecutionState(state);
var stateResult = PInvoke.SetThreadExecutionState(state);
return stateResult != 0;
}
catch
@@ -160,18 +160,18 @@ namespace Awake.Core
bool success;
if (keepDisplayOn)
{
success = SetAwakeState(ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_DISPLAY_REQUIRED | ExecutionState.ES_CONTINUOUS);
success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
}
else
{
success = SetAwakeState(ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_CONTINUOUS);
success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
}
try
{
if (success)
{
_log.Info($"Initiated indefinite keep awake in background thread: {NativeMethods.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
_log.Info($"Initiated indefinite keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
WaitHandle.WaitAny(new[] { _threadToken.WaitHandle });
@@ -186,28 +186,35 @@ namespace Awake.Core
catch (OperationCanceledException ex)
{
// Task was clearly cancelled.
_log.Info($"Background thread termination: {NativeMethods.GetCurrentThreadId()}. Message: {ex.Message}");
_log.Info($"Background thread termination: {PInvoke.GetCurrentThreadId()}. Message: {ex.Message}");
return success;
}
}
internal static void CompleteExit(int exitCode, bool force = false)
internal static void CompleteExit(int exitCode, ManualResetEvent? exitSignal, bool force = false)
{
APIHelper.SetNoKeepAwake();
TrayHelper.ClearTray();
SetNoKeepAwake();
// Because we are running a message loop for the tray, we can't just use Environment.Exit,
// but have to make sure that we properly send the termination message.
IntPtr windowHandle = APIHelper.GetHiddenWindow();
HWND windowHandle = GetHiddenWindow();
if (windowHandle != IntPtr.Zero)
if (windowHandle != HWND.Null)
{
NativeMethods.SendMessage(windowHandle, NativeConstants.WM_CLOSE, 0, string.Empty);
PInvoke.SendMessage(windowHandle, PInvoke.WM_CLOSE, 0, 0);
}
if (force)
{
Environment.Exit(exitCode);
PInvoke.PostQuitMessage(0);
}
try
{
exitSignal?.Set();
PInvoke.DestroyWindow(windowHandle);
}
catch (Exception ex)
{
_log.Info($"Exit signal error ${ex}");
}
}
@@ -221,18 +228,18 @@ namespace Awake.Core
{
if (keepDisplayOn)
{
success = SetAwakeState(ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_DISPLAY_REQUIRED | ExecutionState.ES_CONTINUOUS);
success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
}
else
{
success = SetAwakeState(ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_CONTINUOUS);
success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
}
if (success)
{
_log.Info($"Initiated temporary keep awake in background thread: {NativeMethods.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
_log.Info($"Initiated temporary keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
_timedLoopTimer = new System.Timers.Timer(seconds * 1000);
_timedLoopTimer = new System.Timers.Timer((seconds * 1000) + 1);
_timedLoopTimer.Elapsed += (s, e) =>
{
_tokenSource.Cancel();
@@ -262,7 +269,7 @@ namespace Awake.Core
catch (OperationCanceledException ex)
{
// Task was clearly cancelled.
_log.Info($"Background thread termination: {NativeMethods.GetCurrentThreadId()}. Message: {ex.Message}");
_log.Info($"Background thread termination: {PInvoke.GetCurrentThreadId()}. Message: {ex.Message}");
return success;
}
}
@@ -294,15 +301,20 @@ namespace Awake.Core
}
[SuppressMessage("Performance", "CA1806:Do not ignore method results", Justification = "Function returns DWORD value that identifies the current thread, but we do not need it.")]
public static IEnumerable<IntPtr> EnumerateWindowsForProcess(int processId)
internal static IEnumerable<HWND> EnumerateWindowsForProcess(int processId)
{
var handles = new List<IntPtr>();
IntPtr hCurrentWnd = IntPtr.Zero;
var handles = new List<HWND>();
var hCurrentWnd = HWND.Null;
do
{
hCurrentWnd = NativeMethods.FindWindowEx(IntPtr.Zero, hCurrentWnd, null, null);
NativeMethods.GetWindowThreadProcessId(hCurrentWnd, out uint targetProcessId);
hCurrentWnd = PInvoke.FindWindowEx(HWND.Null, hCurrentWnd, null as string, null);
uint targetProcessId = 0;
unsafe
{
PInvoke.GetWindowThreadProcessId(hCurrentWnd, &targetProcessId);
}
if (targetProcessId == processId)
{
handles.Add(hCurrentWnd);
@@ -314,23 +326,30 @@ namespace Awake.Core
}
[SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "In this context, the string is only converted to a hex value.")]
public static IntPtr GetHiddenWindow()
internal static HWND GetHiddenWindow()
{
IEnumerable<IntPtr> windowHandles = EnumerateWindowsForProcess(Environment.ProcessId);
IEnumerable<HWND> windowHandles = EnumerateWindowsForProcess(Environment.ProcessId);
var domain = AppDomain.CurrentDomain.GetHashCode().ToString("x");
string targetClass = $"{InternalConstants.TrayWindowId}{domain}";
foreach (var handle in windowHandles)
unsafe
{
StringBuilder className = new (256);
int classQueryResult = NativeMethods.GetClassName(handle, className, className.Capacity);
if (classQueryResult != 0 && className.ToString().StartsWith(targetClass, StringComparison.InvariantCultureIgnoreCase))
var classNameLen = 256;
Span<char> className = stackalloc char[classNameLen];
foreach (var handle in windowHandles)
{
return handle;
fixed (char* ptr = className)
{
int classQueryResult = PInvoke.GetClassName(handle, ptr, classNameLen);
if (classQueryResult != 0 && className.ToString().StartsWith(targetClass, StringComparison.InvariantCultureIgnoreCase))
{
return handle;
}
}
}
}
return IntPtr.Zero;
return HWND.Null;
}
public static Dictionary<string, int> GetDefaultTrayOptions()

View File

@@ -1,17 +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;
namespace Awake.Core.Models
{
[Flags]
public enum ExecutionState : uint
{
ES_AWAYMODE_REQUIRED = 0x00000040,
ES_CONTINUOUS = 0x80000000,
ES_DISPLAY_REQUIRED = 0x00000002,
ES_SYSTEM_REQUIRED = 0x00000001,
}
}

View File

@@ -1,70 +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.Runtime.InteropServices;
namespace Awake.Core.Models
{
public struct SystemPowerCapabilities
{
[MarshalAs(UnmanagedType.U1)]
public bool PowerButtonPresent;
[MarshalAs(UnmanagedType.U1)]
public bool SleepButtonPresent;
[MarshalAs(UnmanagedType.U1)]
public bool LidPresent;
[MarshalAs(UnmanagedType.U1)]
public bool SystemS1;
[MarshalAs(UnmanagedType.U1)]
public bool SystemS2;
[MarshalAs(UnmanagedType.U1)]
public bool SystemS3;
[MarshalAs(UnmanagedType.U1)]
public bool SystemS4;
[MarshalAs(UnmanagedType.U1)]
public bool SystemS5;
[MarshalAs(UnmanagedType.U1)]
public bool HiberFilePresent;
[MarshalAs(UnmanagedType.U1)]
public bool FullWake;
[MarshalAs(UnmanagedType.U1)]
public bool VideoDimPresent;
[MarshalAs(UnmanagedType.U1)]
public bool ApmPresent;
[MarshalAs(UnmanagedType.U1)]
public bool UpsPresent;
[MarshalAs(UnmanagedType.U1)]
public bool ThermalControl;
[MarshalAs(UnmanagedType.U1)]
public bool ProcessorThrottle;
public byte ProcessorMinThrottle;
public byte ProcessorMaxThrottle;
[MarshalAs(UnmanagedType.U1)]
public bool FastSystemS4;
[MarshalAs(UnmanagedType.U1)]
public bool Hiberboot;
[MarshalAs(UnmanagedType.U1)]
public bool WakeAlarmPresent;
[MarshalAs(UnmanagedType.U1)]
public bool AoAc;
[MarshalAs(UnmanagedType.U1)]
public bool DiskSpinDown;
public byte HiberFileType;
[MarshalAs(UnmanagedType.U1)]
public bool AoAcConnectivitySupported;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
private readonly byte[] spare3;
[MarshalAs(UnmanagedType.U1)]
public bool SystemBatteriesPresent;
[MarshalAs(UnmanagedType.U1)]
public bool BatteriesAreShortTerm;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public BatteryReportingScale[] BatteryScale;
public SystemPowerState AcOnLineWake;
public SystemPowerState SoftLidWake;
public SystemPowerState RtcWake;
public SystemPowerState MinDeviceWakeState;
public SystemPowerState DefaultLowLatencyWake;
}
}

View File

@@ -1,20 +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.
namespace Awake.Core.Models
{
// Maps to the OS power state.
// See documentation: https://docs.microsoft.com/windows/win32/power/system-power-states
public enum SystemPowerState
{
PowerSystemUnspecified = 0,
PowerSystemWorking = 1,
PowerSystemSleeping1 = 2,
PowerSystemSleeping2 = 3,
PowerSystemSleeping3 = 4,
PowerSystemHibernate = 5,
PowerSystemShutdown = 6,
PowerSystemMaximum = 7,
}
}

View File

@@ -2,14 +2,16 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Windows.Win32;
namespace Awake.Core.Models
{
internal enum TrayCommands : uint
{
TC_DISPLAY_SETTING = NativeConstants.WM_USER + 1,
TC_MODE_PASSIVE = NativeConstants.WM_USER + 2,
TC_MODE_INDEFINITE = NativeConstants.WM_USER + 3,
TC_EXIT = NativeConstants.WM_USER + 4,
TC_TIME = NativeConstants.WM_USER + 5,
TC_DISPLAY_SETTING = PInvoke.WM_USER + 1,
TC_MODE_PASSIVE = PInvoke.WM_USER + 2,
TC_MODE_INDEFINITE = PInvoke.WM_USER + 3,
TC_EXIT = PInvoke.WM_USER + 4,
TC_TIME = PInvoke.WM_USER + 5,
}
}

View File

@@ -1,26 +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.
#pragma warning disable SA1310 // Field names should not contain underscore
namespace Awake.Core
{
internal class NativeConstants
{
internal const uint WM_COMMAND = 0x111;
internal const uint WM_USER = 0x400;
internal const uint WM_GETTEXT = 0x000D;
internal const uint WM_CLOSE = 0x0010;
// Popup menu constants.
internal const uint MF_BYPOSITION = 1024;
internal const uint MF_STRING = 0;
internal const uint MF_MENUBREAK = 0x00000040;
internal const uint MF_SEPARATOR = 0x00000800;
internal const uint MF_POPUP = 0x00000010;
internal const uint MF_UNCHECKED = 0x00000000;
internal const uint MF_CHECKED = 0x00000008;
internal const uint MF_OWNERDRAW = 0x00000100;
}
}

View File

@@ -1,74 +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.Text;
using Awake.Core.Models;
namespace Awake.Core
{
internal static class NativeMethods
{
internal delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("Powrprof.dll", SetLastError = true)]
internal static extern bool GetPwrCapabilities(out SystemPowerCapabilities lpSystemPowerCapabilities);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetConsoleCtrlHandler(ConsoleEventHandler handler, bool add);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern ExecutionState SetThreadExecutionState(ExecutionState esFlags);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern uint GetCurrentThreadId();
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern IntPtr CreateFile(
[MarshalAs(UnmanagedType.LPWStr)] 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);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr CreatePopupMenu();
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool InsertMenu(IntPtr hMenu, uint uPosition, uint uFlags, uint uIDNewItem, string lpNewItem);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool TrackPopupMenuEx(IntPtr hmenu, uint fuFlags, int x, int y, IntPtr hwnd, IntPtr lptpm);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr hWndChildAfter, string? className, string? windowTitle);
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, nuint wParam, string lParam);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool DestroyMenu(IntPtr hMenu);
}
}

View File

@@ -7,11 +7,16 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Awake.Core.Models;
using Microsoft.PowerToys.Settings.UI.Library;
using NLog;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8603 // Possible null reference return.
@@ -22,21 +27,18 @@ namespace Awake.Core
{
private static readonly Logger _log;
private static IntPtr _trayMenu;
private static DestroyMenuSafeHandle TrayMenu { get; set; }
private static IntPtr TrayMenu { get => _trayMenu; set => _trayMenu = value; }
private static NotifyIcon? _trayIcon;
private static NotifyIcon TrayIcon { get => _trayIcon; set => _trayIcon = value; }
private static NotifyIcon TrayIcon { get; set; }
static TrayHelper()
{
_log = LogManager.GetCurrentClassLogger();
TrayMenu = new DestroyMenuSafeHandle();
TrayIcon = new NotifyIcon();
}
public static void InitializeTray(string text, Icon icon, ContextMenuStrip? contextMenu = null)
public static void InitializeTray(string text, Icon icon, ManualResetEvent? exitSignal, ContextMenuStrip? contextMenu = null)
{
Task.Factory.StartNew(
(tray) =>
@@ -49,7 +51,7 @@ namespace Awake.Core
((NotifyIcon?)tray).ContextMenuStrip = contextMenu;
((NotifyIcon?)tray).Visible = true;
((NotifyIcon?)tray).MouseClick += TrayClickHandler;
Application.AddMessageFilter(new TrayMessageFilter());
Application.AddMessageFilter(new TrayMessageFilter(exitSignal));
Application.Run();
_log.Info("Tray setup complete.");
}
@@ -74,21 +76,15 @@ namespace Awake.Core
/// <param name="e">MouseEventArgs instance containing mouse click event information.</param>
private static void TrayClickHandler(object? sender, MouseEventArgs e)
{
IntPtr windowHandle = APIHelper.GetHiddenWindow();
HWND windowHandle = APIHelper.GetHiddenWindow();
if (windowHandle != IntPtr.Zero)
if (windowHandle != HWND.Null)
{
NativeMethods.SetForegroundWindow(windowHandle);
NativeMethods.TrackPopupMenuEx(TrayMenu, 0, Cursor.Position.X, Cursor.Position.Y, windowHandle, IntPtr.Zero);
PInvoke.SetForegroundWindow(windowHandle);
PInvoke.TrackPopupMenuEx(TrayMenu, 0, Cursor.Position.X, Cursor.Position.Y, windowHandle, null);
}
}
public static void ClearTray()
{
TrayIcon.Icon = null;
TrayIcon.Dispose();
}
internal static void SetTray(string text, AwakeSettings settings)
{
SetTray(
@@ -101,19 +97,14 @@ namespace Awake.Core
[SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1005:Single line comments should begin with single space", Justification = "For debugging purposes - will remove later.")]
public static void SetTray(string text, bool keepDisplayOn, AwakeMode mode, Dictionary<string, int> trayTimeShortcuts)
{
if (TrayMenu != IntPtr.Zero)
{
var destructionStatus = NativeMethods.DestroyMenu(TrayMenu);
if (destructionStatus != true)
{
_log.Error("Failed to destroy tray menu and free up memory.");
}
}
TrayMenu = new DestroyMenuSafeHandle(PInvoke.CreatePopupMenu());
TrayMenu = NativeMethods.CreatePopupMenu();
NativeMethods.InsertMenu(TrayMenu, 0, NativeConstants.MF_BYPOSITION | NativeConstants.MF_STRING, (uint)TrayCommands.TC_EXIT, "Exit");
NativeMethods.InsertMenu(TrayMenu, 0, NativeConstants.MF_BYPOSITION | NativeConstants.MF_SEPARATOR, 0, string.Empty);
NativeMethods.InsertMenu(TrayMenu, 0, NativeConstants.MF_BYPOSITION | NativeConstants.MF_STRING | (keepDisplayOn ? NativeConstants.MF_CHECKED : NativeConstants.MF_UNCHECKED), (uint)TrayCommands.TC_DISPLAY_SETTING, "Keep screen on");
if (!TrayMenu.IsInvalid)
{
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING, (uint)TrayCommands.TC_EXIT, "Exit");
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_SEPARATOR, 0, string.Empty);
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (keepDisplayOn ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_DISPLAY_SETTING, "Keep screen on");
}
// In case there are no tray shortcuts defined for the application default to a
// reasonable initial set.
@@ -123,18 +114,18 @@ namespace Awake.Core
}
// TODO: Make sure that this loads from JSON instead of being hard-coded.
var awakeTimeMenu = NativeMethods.CreatePopupMenu();
var awakeTimeMenu = new DestroyMenuSafeHandle(PInvoke.CreatePopupMenu(), false);
for (int i = 0; i < trayTimeShortcuts.Count; i++)
{
NativeMethods.InsertMenu(awakeTimeMenu, (uint)i, NativeConstants.MF_BYPOSITION | NativeConstants.MF_STRING, (uint)TrayCommands.TC_TIME + (uint)i, trayTimeShortcuts.ElementAt(i).Key);
PInvoke.InsertMenu(awakeTimeMenu, (uint)i, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING, (uint)TrayCommands.TC_TIME + (uint)i, trayTimeShortcuts.ElementAt(i).Key);
}
var modeMenu = NativeMethods.CreatePopupMenu();
NativeMethods.InsertMenu(modeMenu, 0, NativeConstants.MF_BYPOSITION | NativeConstants.MF_STRING | (mode == AwakeMode.PASSIVE ? NativeConstants.MF_CHECKED : NativeConstants.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_PASSIVE, "Off (keep using the selected power plan)");
NativeMethods.InsertMenu(modeMenu, 1, NativeConstants.MF_BYPOSITION | NativeConstants.MF_STRING | (mode == AwakeMode.INDEFINITE ? NativeConstants.MF_CHECKED : NativeConstants.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_INDEFINITE, "Keep awake indefinitely");
var modeMenu = new DestroyMenuSafeHandle(PInvoke.CreatePopupMenu(), false);
PInvoke.InsertMenu(modeMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (mode == AwakeMode.PASSIVE ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_PASSIVE, "Off (keep using the selected power plan)");
PInvoke.InsertMenu(modeMenu, 1, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (mode == AwakeMode.INDEFINITE ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_INDEFINITE, "Keep awake indefinitely");
NativeMethods.InsertMenu(modeMenu, 2, NativeConstants.MF_BYPOSITION | NativeConstants.MF_POPUP | (mode == AwakeMode.TIMED ? NativeConstants.MF_CHECKED : NativeConstants.MF_UNCHECKED), (uint)awakeTimeMenu, "Keep awake temporarily");
NativeMethods.InsertMenu(TrayMenu, 0, NativeConstants.MF_BYPOSITION | NativeConstants.MF_POPUP, (uint)modeMenu, "Mode");
PInvoke.InsertMenu(modeMenu, 2, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_POPUP | (mode == AwakeMode.TIMED ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)awakeTimeMenu.DangerousGetHandle(), "Keep awake temporarily");
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_POPUP, (uint)modeMenu.DangerousGetHandle(), "Mode");
TrayIcon.Text = text;
}

View File

@@ -6,9 +6,11 @@ using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Windows.Forms;
using Awake.Core.Models;
using Microsoft.PowerToys.Settings.UI.Library;
using Windows.Win32;
#pragma warning disable CS8603 // Possible null reference return.
@@ -20,8 +22,11 @@ namespace Awake.Core
private static SettingsUtils ModuleSettings { get => _moduleSettings; set => _moduleSettings = value; }
public TrayMessageFilter()
private static ManualResetEvent? _exitSignal;
public TrayMessageFilter(ManualResetEvent? exitSignal)
{
_exitSignal = exitSignal;
ModuleSettings = new SettingsUtils();
}
@@ -31,12 +36,12 @@ namespace Awake.Core
switch (m.Msg)
{
case (int)NativeConstants.WM_COMMAND:
case (int)PInvoke.WM_COMMAND:
var targetCommandIndex = m.WParam.ToInt64() & 0xFFFF;
switch (targetCommandIndex)
{
case (long)TrayCommands.TC_EXIT:
ExitCommandHandler();
ExitCommandHandler(_exitSignal);
break;
case (long)TrayCommands.TC_DISPLAY_SETTING:
DisplaySettingCommandHandler(InternalConstants.AppName);
@@ -68,9 +73,9 @@ namespace Awake.Core
return false;
}
private static void ExitCommandHandler()
private static void ExitCommandHandler(ManualResetEvent? exitSignal)
{
APIHelper.CompleteExit(0, true);
APIHelper.CompleteExit(0, exitSignal, true);
}
private static void DisplaySettingCommandHandler(string moduleName)

View File

@@ -0,0 +1,23 @@
AllocConsole
CreateFile
CreatePopupMenu
DestroyMenu
DestroyWindow
FindWindowEx
GetClassName
GetCurrentThreadId
GetPwrCapabilities
GetWindowThreadProcessId
HMENU
InsertMenu
PostQuitMessage
SendMessage
SetConsoleCtrlHandler
SetForegroundWindow
SetStdHandle
SetThreadExecutionState
TrackPopupMenuEx
WM_CLOSE
WM_COMMAND
WM_GETTEXT
WM_USER

View File

@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Diagnostics;
@@ -17,11 +16,15 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Awake.Core;
using Awake.Core.Models;
using interop;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Telemetry.Events;
using NLog;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.System.Console;
using Windows.Win32.System.Power;
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8603 // Possible null reference return.
@@ -47,8 +50,8 @@ namespace Awake
private static Logger? _log;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private static ConsoleEventHandler _handler;
private static SystemPowerCapabilities _powerCapabilities;
private static PHANDLER_ROUTINE _handler;
private static SYSTEM_POWER_CAPABILITIES _powerCapabilities;
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private static ManualResetEvent _exitSignal = new ManualResetEvent(false);
@@ -63,7 +66,7 @@ namespace Awake
if (!instantiated)
{
Exit(InternalConstants.AppName + " is already running! Exiting the application.", 1, true);
Exit(InternalConstants.AppName + " is already running! Exiting the application.", 1, _exitSignal, true);
}
_settingsUtils = new SettingsUtils();
@@ -82,12 +85,12 @@ namespace Awake
// To make it easier to diagnose future issues, let's get the
// system power capabilities and aggregate them in the log.
NativeMethods.GetPwrCapabilities(out _powerCapabilities);
PInvoke.GetPwrCapabilities(out _powerCapabilities);
_log.Info(JsonSerializer.Serialize(_powerCapabilities));
_log.Info("Parsing parameters...");
Option<bool>? configOption = new (
var configOption = new Option<bool>(
aliases: new[] { "--use-pt-config", "-c" },
getDefaultValue: () => false,
description: $"Specifies whether {InternalConstants.AppName} will be using the PowerToys configuration file for managing the state.")
@@ -100,7 +103,7 @@ namespace Awake
configOption.Required = false;
Option<bool>? displayOption = new (
var displayOption = new Option<bool>(
aliases: new[] { "--display-on", "-d" },
getDefaultValue: () => true,
description: "Determines whether the display should be kept awake.")
@@ -113,7 +116,7 @@ namespace Awake
displayOption.Required = false;
Option<uint>? timeOption = new (
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.")
@@ -126,7 +129,7 @@ namespace Awake
timeOption.Required = false;
Option<int>? pidOption = new (
var pidOption = new Option<int>(
aliases: new[] { "--pid", "-p" },
getDefaultValue: () => 0,
description: $"Bind the execution of {InternalConstants.AppName} to another process.")
@@ -156,23 +159,23 @@ namespace Awake
return rootCommand.InvokeAsync(args).Result;
}
private static bool ExitHandler(ControlType ctrlType)
private static BOOL ExitHandler(uint ctrlType)
{
_log.Info($"Exited through handler with control type: {ctrlType}");
Exit("Exiting from the internal termination handler.", Environment.ExitCode);
Exit("Exiting from the internal termination handler.", Environment.ExitCode, _exitSignal);
return false;
}
private static void Exit(string message, int exitCode, bool force = false)
private static void Exit(string message, int exitCode, ManualResetEvent exitSignal, bool force = false)
{
_log.Info(message);
APIHelper.CompleteExit(exitCode, force);
APIHelper.CompleteExit(exitCode, exitSignal, force);
}
private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, uint timeLimit, int pid)
{
_handler += new ConsoleEventHandler(ExitHandler);
_handler += ExitHandler;
APIHelper.SetConsoleControlHandler(_handler, true);
if (pid == 0)
@@ -192,16 +195,15 @@ namespace Awake
// and instead watch for changes in the file.
try
{
var eventHandle = new EventWaitHandle(false, EventResetMode.ManualReset, Constants.AwakeExitEvent());
new Thread(() =>
{
EventWaitHandle? eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.AwakeExitEvent());
if (eventHandle.WaitOne())
if (WaitHandle.WaitAny(new WaitHandle[] { _exitSignal, eventHandle }) == 1)
{
Exit("Received a signal to end the process. Making sure we quit...", 0, true);
Exit("Received a signal to end the process. Making sure we quit...", 0, _exitSignal, true);
}
}).Start();
TrayHelper.InitializeTray(InternalConstants.FullAppName, new Icon("modules/awake/images/awake.ico"));
TrayHelper.InitializeTray(InternalConstants.FullAppName, new Icon("modules/awake/images/awake.ico"), _exitSignal);
string? settingsPath = _settingsUtils.GetSettingsFilePath(InternalConstants.AppName);
_log.Info($"Reading configuration file: {settingsPath}");
@@ -263,7 +265,7 @@ namespace Awake
RunnerHelper.WaitForPowerToysRunner(pid, () =>
{
_log.Info($"Triggered PID-based exit handler for PID {pid}.");
Exit("Terminating from process binding hook.", 0, true);
Exit("Terminating from process binding hook.", 0, _exitSignal, true);
});
}

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel.Composition;
using System.Threading;
using System.Windows;
using ColorPicker.Helpers;
@@ -23,8 +24,16 @@ namespace ColorPickerUI
private bool disposedValue;
private ThemeManager _themeManager;
private CancellationTokenSource NativeThreadCTS { get; set; }
[Export]
private static CancellationToken ExitToken { get; set; }
protected override void OnStartup(StartupEventArgs e)
{
NativeThreadCTS = new CancellationTokenSource();
ExitToken = NativeThreadCTS.Token;
_args = e?.Args;
// allow only one instance of color picker
@@ -33,7 +42,7 @@ namespace ColorPickerUI
{
Logger.LogWarning("There is ColorPicker instance running. Exiting Color Picker");
_instanceMutex = null;
Environment.Exit(0);
Shutdown(0);
return;
}
@@ -45,7 +54,8 @@ namespace ColorPickerUI
RunnerHelper.WaitForPowerToysRunner(_powerToysRunnerPid, () =>
{
Logger.LogInfo("PowerToys Runner exited. Exiting ColorPicker");
Environment.Exit(0);
NativeThreadCTS.Cancel();
Dispatcher.Invoke(Shutdown);
});
}
else

View File

@@ -7,7 +7,6 @@ using System.Globalization;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using ColorPicker.Helpers;

View File

@@ -3,11 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace ColorPicker.Controls
{

View File

@@ -4,7 +4,6 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;

View File

@@ -1,29 +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.Threading;
using System.Windows;
namespace ColorPicker.Helpers
{
public static class NativeEventWaiter
{
public static void WaitForEventLoop(string eventName, Action callback)
{
new Thread(() =>
{
var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
while (true)
{
if (eventHandle.WaitOne())
{
Logger.LogInfo($"Successfully waited for {eventName}");
Application.Current.Dispatcher.Invoke(callback);
}
}
}).Start();
}
}
}

View File

@@ -7,11 +7,8 @@ using System.ComponentModel.Composition;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows;
using System.Windows.Media.Imaging;
using ColorPicker.Telemetry;
using ColorPicker.ViewModelContracts;
using Microsoft.PowerToys.Telemetry;
namespace ColorPicker.Helpers
{

View File

@@ -8,10 +8,7 @@ using System.ComponentModel.Composition;
using System.Windows.Input;
using ColorPicker.Helpers;
using ColorPicker.Settings;
using ColorPicker.Telemetry;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Microsoft.PowerToys.Telemetry;
using static ColorPicker.NativeMethods;
namespace ColorPicker.Keyboard

View File

@@ -5,7 +5,6 @@
using System;
using ColorPicker.Helpers;
using ColorPicker.Mouse;
using ColorPickerUI;
namespace ColorPicker

View File

@@ -2,7 +2,6 @@
// 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.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;

View File

@@ -4,7 +4,6 @@
using System;
using System.ComponentModel.Composition;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Media;
@@ -13,11 +12,9 @@ using ColorPicker.Helpers;
using ColorPicker.Keyboard;
using ColorPicker.Mouse;
using ColorPicker.Settings;
using ColorPicker.Telemetry;
using ColorPicker.ViewModelContracts;
using Common.UI;
using interop;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using Microsoft.PowerToys.Telemetry;
namespace ColorPicker.ViewModels
{
@@ -49,13 +46,24 @@ namespace ColorPicker.ViewModels
ZoomWindowHelper zoomWindowHelper,
AppStateHandler appStateHandler,
KeyboardMonitor keyboardMonitor,
IUserSettings userSettings)
IUserSettings userSettings,
CancellationToken exitToken)
{
_zoomWindowHelper = zoomWindowHelper;
_appStateHandler = appStateHandler;
_userSettings = userSettings;
NativeEventWaiter.WaitForEventLoop(Constants.ShowColorPickerSharedEvent(), _appStateHandler.StartUserSession);
NativeEventWaiter.WaitForEventLoop(Constants.ColorPickerSendSettingsTelemetryEvent(), _userSettings.SendSettingsTelemetry);
NativeEventWaiter.WaitForEventLoop(
Constants.ShowColorPickerSharedEvent(),
_appStateHandler.StartUserSession,
Application.Current.Dispatcher,
exitToken);
NativeEventWaiter.WaitForEventLoop(
Constants.ColorPickerSendSettingsTelemetryEvent(),
_userSettings.SendSettingsTelemetry,
Application.Current.Dispatcher,
exitToken);
if (mouseInfoProvider != null)
{

View File

@@ -2,7 +2,6 @@
// 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.ComponentModel;
using System.Windows;
namespace ColorPicker

View File

@@ -5,9 +5,9 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using Common.UI;
using FancyZonesEditor.Logs;
using FancyZonesEditor.Utils;
@@ -35,10 +35,6 @@ namespace FancyZonesEditor
private ThemeManager _themeManager;
private EventWaitHandle _eventHandle;
private Thread _exitWaitThread;
public static bool DebugMode
{
get
@@ -50,6 +46,8 @@ namespace FancyZonesEditor
private static bool _debugMode;
private bool _isDisposed;
private CancellationTokenSource NativeThreadCTS { get; set; }
[Conditional("DEBUG")]
private void DebugModeCheck()
{
@@ -59,27 +57,29 @@ namespace FancyZonesEditor
public App()
{
// DebugModeCheck();
NativeThreadCTS = new CancellationTokenSource();
FancyZonesEditorIO = new FancyZonesEditorIO();
Overlay = new Overlay();
MainWindowSettings = new MainWindowSettingsModel();
_exitWaitThread = new Thread(App_WaitExit);
_exitWaitThread.Start();
App_WaitExit();
}
private void OnStartup(object sender, StartupEventArgs e)
{
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () =>
{
Logger.LogInfo("Runner exited");
Environment.Exit(0);
});
_themeManager = new ThemeManager(this);
var parseResult = FancyZonesEditorIO.ParseParams();
RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () =>
{
Logger.LogInfo("Runner exited");
NativeThreadCTS.Cancel();
Application.Current.Dispatcher.Invoke(Application.Current.Shutdown);
});
if (!parseResult.Result)
{
Logger.LogError(ParsingErrorReportTag + ": " + parseResult.Message + "; " + ParsingErrorDataTag + ": " + parseResult.MalformedData);
@@ -122,26 +122,23 @@ namespace FancyZonesEditor
private void OnExit(object sender, ExitEventArgs e)
{
NativeThreadCTS.Cancel();
Dispose();
if (_eventHandle != null)
{
_eventHandle.Set();
}
_exitWaitThread.Join();
Logger.LogInfo("FancyZones Editor exited");
}
private void App_WaitExit()
{
_eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, interop.Constants.FZEExitEvent());
if (_eventHandle.WaitOne())
NativeEventWaiter.WaitForEventLoop(
interop.Constants.FZEExitEvent(),
() =>
{
Logger.LogInfo("Exit event triggered");
Environment.Exit(0);
}
Application.Current.Shutdown();
},
Current.Dispatcher,
NativeThreadCTS.Token);
}
public void App_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)

View File

@@ -31,7 +31,10 @@ namespace PowerLauncher
{
public static PublicAPIInstance API { get; private set; }
public static CancellationTokenSource NativeThreadCTS { get; private set; }
private static bool _disposed;
private PowerToysRunSettings _settings;
private MainViewModel _mainVM;
private MainWindow _mainWindow;
@@ -46,6 +49,8 @@ namespace PowerLauncher
[STAThread]
public static void Main()
{
NativeThreadCTS = new CancellationTokenSource();
Log.Info($"Starting PowerToys Run with PID={Environment.ProcessId}", typeof(App));
int powerToysPid = GetPowerToysPId();
if (powerToysPid != 0)
@@ -67,15 +72,16 @@ namespace PowerLauncher
using (var application = new App())
{
application.InitializeComponent();
new Thread(() =>
{
var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.RunExitEvent());
if (eventHandle.WaitOne())
NativeEventWaiter.WaitForEventLoop(
Constants.RunExitEvent(),
() =>
{
Log.Warn("RunExitEvent was signaled. Exiting PowerToys", typeof(App));
ExitPowerToys(application);
}
}).Start();
},
Application.Current.Dispatcher,
NativeThreadCTS.Token);
if (powerToysPid != 0)
{
@@ -118,8 +124,8 @@ namespace PowerLauncher
StringMatcher.Instance = _stringMatcher;
_stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision;
_mainVM = new MainViewModel(_settings);
_mainWindow = new MainWindow(_settings, _mainVM);
_mainVM = new MainViewModel(_settings, NativeThreadCTS.Token);
_mainWindow = new MainWindow(_settings, _mainVM, NativeThreadCTS.Token);
API = new PublicAPIInstance(_settingsVM, _mainVM, _themeManager);
_settingsReader = new SettingsReader(_settings, _themeManager);
_settingsReader.ReadSettings();
@@ -154,14 +160,7 @@ namespace PowerLauncher
{
SingleInstance<App>.SingleInstanceMutex.Close();
try
{
app.Dispose();
}
finally
{
Environment.Exit(0);
}
app.Dispatcher.Invoke(() => app.Shutdown());
}
private static int GetPowerToysPId()
@@ -194,6 +193,7 @@ namespace PowerLauncher
Current.Exit += (s, e) =>
{
NativeThreadCTS.Cancel();
Log.Info("Application.Current.Exit", GetType());
Dispose();
};

View File

@@ -1,31 +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.Reflection;
using System.Threading;
using System.Windows;
using Wox.Plugin.Logger;
namespace PowerLauncher.Helper
{
public static class NativeEventWaiter
{
public static void WaitForEventLoop(string eventName, Action callback)
{
new Thread(() =>
{
var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
while (true)
{
if (eventHandle.WaitOne())
{
Log.Info($"Successfully waited for {eventName}", MethodBase.GetCurrentMethod().DeclaringType);
Application.Current.Dispatcher.Invoke(callback);
}
}
}).Start();
}
}
}

View File

@@ -12,6 +12,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using Common.UI;
using interop;
using Microsoft.PowerLauncher.Telemetry;
using Microsoft.PowerToys.Telemetry;
@@ -20,6 +21,7 @@ using PowerLauncher.Plugin;
using PowerLauncher.Telemetry.Events;
using PowerLauncher.ViewModel;
using Wox.Infrastructure.UserSettings;
using CancellationToken = System.Threading.CancellationToken;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
using Log = Wox.Plugin.Logger.Log;
using Screen = System.Windows.Forms.Screen;
@@ -30,6 +32,7 @@ namespace PowerLauncher
{
private readonly PowerToysRunSettings _settings;
private readonly MainViewModel _viewModel;
private readonly CancellationToken _nativeWaiterCancelToken;
private bool _isTextSetProgrammatically;
private bool _deletePressed;
private HwndSource _hwndSource;
@@ -38,18 +41,23 @@ namespace PowerLauncher
private bool _disposedValue;
private IDisposable _reactiveSubscription;
public MainWindow(PowerToysRunSettings settings, MainViewModel mainVM)
public MainWindow(PowerToysRunSettings settings, MainViewModel mainVM, CancellationToken nativeWaiterCancelToken)
: this()
{
DataContext = mainVM;
_viewModel = mainVM;
_nativeWaiterCancelToken = nativeWaiterCancelToken;
_settings = settings;
InitializeComponent();
_firstDeleteTimer.Elapsed += CheckForFirstDelete;
_firstDeleteTimer.Interval = 1000;
NativeEventWaiter.WaitForEventLoop(Constants.RunSendSettingsTelemetryEvent(), SendSettingsTelemetry);
NativeEventWaiter.WaitForEventLoop(
Constants.RunSendSettingsTelemetryEvent(),
SendSettingsTelemetry,
Application.Current.Dispatcher,
_nativeWaiterCancelToken);
}
private void SendSettingsTelemetry()
@@ -698,7 +706,15 @@ namespace PowerLauncher
private void OnClosed(object sender, EventArgs e)
{
_hwndSource.RemoveHook(ProcessWindowMessages);
try
{
_hwndSource.RemoveHook(ProcessWindowMessages);
}
catch (Exception ex)
{
Log.Exception($"Exception when trying to Remove hook", ex, ex.GetType());
}
_hwndSource = null;
}
}

View File

@@ -49,19 +49,6 @@ namespace Wox
_mainVM.ChangeQueryText(query, requery);
}
public void RestartApp()
{
_mainVM.MainWindowVisibility = Visibility.Hidden;
// we must manually save
// UpdateManager.RestartApp() will call Environment.Exit(0)
// which will cause ungraceful exit
SaveAppAllSettings();
// Todo : Implement logic to restart this app.
Environment.Exit(0);
}
public void CheckForNewUpdate()
{
// _settingsVM.UpdateApp();

View File

@@ -12,6 +12,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using Common.UI;
using interop;
using Microsoft.PowerLauncher.Telemetry;
using Microsoft.PowerToys.Telemetry;
@@ -48,6 +49,7 @@ namespace PowerLauncher.ViewModel
private CancellationTokenSource _updateSource;
private CancellationToken _updateToken;
private CancellationToken _nativeWaiterCancelToken;
private bool _saved;
private ushort _hotkeyHandle;
@@ -59,7 +61,7 @@ namespace PowerLauncher.ViewModel
internal HotkeyManager HotkeyManager { get; private set; }
public MainViewModel(PowerToysRunSettings settings)
public MainViewModel(PowerToysRunSettings settings, CancellationToken nativeThreadCancelToken)
{
_saved = false;
_queryTextBeforeLeaveResults = string.Empty;
@@ -67,7 +69,7 @@ namespace PowerLauncher.ViewModel
_disposed = false;
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
_nativeWaiterCancelToken = nativeThreadCancelToken;
_historyItemsStorage = new WoxJsonStorage<QueryHistory>();
_userSelectedRecordStorage = new WoxJsonStorage<UserSelectedRecord>();
_history = _historyItemsStorage.Load();
@@ -92,12 +94,12 @@ namespace PowerLauncher.ViewModel
Log.Info("RegisterHotkey()", GetType());
// Allow OOBE to call PowerToys Run.
NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherSharedEvent(), OnHotkey);
NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherSharedEvent(), OnHotkey, Application.Current.Dispatcher, _nativeWaiterCancelToken);
if (_settings.StartedFromPowerToysRunner)
{
// Allow runner to call PowerToys Run from the centralized keyboard hook.
NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherCentralizedHookSharedEvent(), OnCentralizedKeyboardHookHotKey);
NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherCentralizedHookSharedEvent(), OnCentralizedKeyboardHookHotKey, Application.Current.Dispatcher, _nativeWaiterCancelToken);
}
_settings.PropertyChanged += (s, e) =>

View File

@@ -23,11 +23,6 @@ namespace Wox.Plugin
/// </param>
void ChangeQuery(string query, bool requery = false);
/// <summary>
/// Restart Wox
/// </summary>
void RestartApp();
/// <summary>
/// Remove user selected history item and refresh/requery
/// </summary>

View File

@@ -1,27 +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 Vanara.PInvoke;
namespace PowerAccent.Core;
public enum LetterKey
{
A = User32.VK.VK_A,
C = User32.VK.VK_C,
E = User32.VK.VK_E,
I = User32.VK.VK_I,
N = User32.VK.VK_N,
O = User32.VK.VK_O,
S = User32.VK.VK_S,
U = User32.VK.VK_U,
Y = User32.VK.VK_Y,
}
public enum TriggerKey
{
Left = User32.VK.VK_LEFT,
Right = User32.VK.VK_RIGHT,
Space = User32.VK.VK_SPACE,
}

View File

@@ -1,20 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\Version.props" />
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<Import Project="..\..\..\Version.props" />
<PropertyGroup>
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<UseWPF>true</UseWPF>
</PropertyGroup>
<PropertyGroup>
<CsWinRTIncludes>PowerToys.PowerAccentKeyboardService</CsWinRTIncludes>
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
<PackageReference Include="Vanara.PInvoke.User32" Version="3.3.15" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.0.0" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
<PackageReference Include="Vanara.PInvoke.User32" Version="3.3.15" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
<ProjectReference Include="..\PowerAccentKeyboardService\PowerAccentKeyboardService.vcxproj" />
</ItemGroup>
</Project>

View File

@@ -2,173 +2,159 @@
// 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.Diagnostics;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using System.Windows;
using PowerAccent.Core.Services;
using PowerAccent.Core.Tools;
using PowerToys.PowerAccentKeyboardService;
namespace PowerAccent.Core;
public class PowerAccent : IDisposable
{
private readonly SettingsService _settingService = new SettingsService();
private readonly KeyboardListener _keyboardListener = new KeyboardListener();
private readonly SettingsService _settingService;
private LetterKey? letterPressed;
private bool _visible;
private char[] _characters = Array.Empty<char>();
private int _selectedIndex = -1;
private Stopwatch _stopWatch;
private bool _triggeredWithSpace;
public event Action<bool, char[]> OnChangeDisplay;
public event Action<int, char> OnSelectCharacter;
private KeyboardListener _keyboardListener;
public PowerAccent()
{
_keyboardListener.KeyDown += PowerAccent_KeyDown;
_keyboardListener.KeyUp += PowerAccent_KeyUp;
_keyboardListener = new KeyboardListener();
_keyboardListener.InitHook();
_settingService = new SettingsService(_keyboardListener);
SetEvents();
}
private bool PowerAccent_KeyDown(object sender, KeyboardListener.RawKeyEventArgs args)
private void SetEvents()
{
if (Enum.IsDefined(typeof(LetterKey), (int)args.Key))
_keyboardListener.SetShowToolbarEvent(new PowerToys.PowerAccentKeyboardService.ShowToolbar((LetterKey letterKey) =>
{
_stopWatch = Stopwatch.StartNew();
letterPressed = (LetterKey)args.Key;
}
TriggerKey? triggerPressed = null;
if (letterPressed.HasValue)
{
if (Enum.IsDefined(typeof(TriggerKey), (int)args.Key))
Application.Current.Dispatcher.Invoke(() =>
{
triggerPressed = (TriggerKey)args.Key;
ShowToolbar(letterKey);
});
}));
if ((triggerPressed == TriggerKey.Space && _settingService.ActivationKey == PowerAccentActivationKey.LeftRightArrow) ||
((triggerPressed == TriggerKey.Left || triggerPressed == TriggerKey.Right) && _settingService.ActivationKey == PowerAccentActivationKey.Space))
{
triggerPressed = null;
}
}
}
if (!_visible && letterPressed.HasValue && triggerPressed.HasValue)
_keyboardListener.SetHideToolbarEvent(new PowerToys.PowerAccentKeyboardService.HideToolbar((InputType inputType) =>
{
// Keep track if it was triggered with space so that it can be typed on false starts.
_triggeredWithSpace = triggerPressed.Value == TriggerKey.Space;
_visible = true;
_characters = WindowsFunctions.IsCapitalState() ? ToUpper(_settingService.GetLetterKey(letterPressed.Value)) : _settingService.GetLetterKey(letterPressed.Value);
Task.Delay(_settingService.InputTime).ContinueWith(
t =>
Application.Current.Dispatcher.Invoke(() =>
{
SendInputAndHideToolbar(inputType);
});
}));
_keyboardListener.SetNextCharEvent(new PowerToys.PowerAccentKeyboardService.NextChar((TriggerKey triggerKey) =>
{
Application.Current.Dispatcher.Invoke(() =>
{
ProcessNextChar(triggerKey);
});
}));
}
private void ShowToolbar(LetterKey letterKey)
{
_visible = true;
_characters = WindowsFunctions.IsCapitalState() ? ToUpper(SettingsService.GetDefaultLetterKey(letterKey)) : SettingsService.GetDefaultLetterKey(letterKey);
Task.Delay(_settingService.InputTime).ContinueWith(
t =>
{
if (_visible)
{
if (_visible)
OnChangeDisplay?.Invoke(true, _characters);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
private void SendInputAndHideToolbar(InputType inputType)
{
switch (inputType)
{
case InputType.Space:
{
WindowsFunctions.Insert(' ');
break;
}
case InputType.Char:
{
if (_selectedIndex != -1)
{
OnChangeDisplay?.Invoke(true, _characters);
WindowsFunctions.Insert(_characters[_selectedIndex], true);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
break;
}
}
if (_visible && triggerPressed.HasValue)
OnChangeDisplay?.Invoke(false, null);
_selectedIndex = -1;
_visible = false;
}
private void ProcessNextChar(TriggerKey triggerKey)
{
if (_visible && _selectedIndex == -1)
{
if (_selectedIndex == -1)
if (triggerKey == TriggerKey.Left)
{
if (triggerPressed.Value == TriggerKey.Left)
{
_selectedIndex = (_characters.Length / 2) - 1;
}
if (triggerPressed.Value == TriggerKey.Right)
{
_selectedIndex = _characters.Length / 2;
}
if (triggerPressed.Value == TriggerKey.Space)
{
_selectedIndex = 0;
}
if (_selectedIndex < 0)
{
_selectedIndex = 0;
}
if (_selectedIndex > _characters.Length - 1)
{
_selectedIndex = _characters.Length - 1;
}
OnSelectCharacter?.Invoke(_selectedIndex, _characters[_selectedIndex]);
return false;
_selectedIndex = (_characters.Length / 2) - 1;
}
if (triggerPressed.Value == TriggerKey.Space)
if (triggerKey == TriggerKey.Right)
{
if (_selectedIndex < _characters.Length - 1)
{
++_selectedIndex;
}
else
{
_selectedIndex = 0;
}
_selectedIndex = _characters.Length / 2;
}
if (triggerPressed.Value == TriggerKey.Left && _selectedIndex > 0)
if (triggerKey == TriggerKey.Space)
{
--_selectedIndex;
_selectedIndex = 0;
}
if (triggerPressed.Value == TriggerKey.Right && _selectedIndex < _characters.Length - 1)
if (_selectedIndex < 0)
{
++_selectedIndex;
_selectedIndex = 0;
}
if (_selectedIndex > _characters.Length - 1)
{
_selectedIndex = _characters.Length - 1;
}
OnSelectCharacter?.Invoke(_selectedIndex, _characters[_selectedIndex]);
return false;
return;
}
return true;
}
private bool PowerAccent_KeyUp(object sender, KeyboardListener.RawKeyEventArgs args)
{
if (Enum.IsDefined(typeof(LetterKey), (int)args.Key))
if (triggerKey == TriggerKey.Space)
{
letterPressed = null;
_stopWatch.Stop();
if (_visible)
if (_selectedIndex < _characters.Length - 1)
{
if (_stopWatch.ElapsedMilliseconds < _settingService.InputTime)
{
/* Debug.WriteLine("Insert before inputTime - " + _stopWatch.ElapsedMilliseconds); */
// False start, we should output the space if it was the trigger.
if (_triggeredWithSpace)
{
WindowsFunctions.Insert(' ');
}
OnChangeDisplay?.Invoke(false, null);
_selectedIndex = -1;
_visible = false;
return false;
}
/* Debug.WriteLine("Insert after inputTime - " + _stopWatch.ElapsedMilliseconds); */
OnChangeDisplay?.Invoke(false, null);
if (_selectedIndex != -1)
{
WindowsFunctions.Insert(_characters[_selectedIndex], true);
}
_selectedIndex = -1;
_visible = false;
++_selectedIndex;
}
else
{
_selectedIndex = 0;
}
}
return true;
if (triggerKey == TriggerKey.Left && _selectedIndex > 0)
{
--_selectedIndex;
}
if (triggerKey == TriggerKey.Right && _selectedIndex < _characters.Length - 1)
{
++_selectedIndex;
}
OnSelectCharacter?.Invoke(_selectedIndex, _characters[_selectedIndex]);
}
public Point GetDisplayCoordinates(Size window)
@@ -182,14 +168,9 @@ public class PowerAccent : IDisposable
return Calculation.GetRawCoordinatesFromPosition(position, screen, window);
}
public char[] GetLettersFromKey(LetterKey letter)
{
return _settingService.GetLetterKey(letter);
}
public void Dispose()
{
_keyboardListener.Dispose();
_keyboardListener.UnInitHook();
GC.SuppressFinalize(this);
}

View File

@@ -7,6 +7,7 @@ namespace PowerAccent.Core.Services;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using PowerToys.PowerAccentKeyboardService;
using System.IO.Abstractions;
using System.Text.Json;
@@ -16,10 +17,12 @@ public class SettingsService
private readonly ISettingsUtils _settingsUtils;
private readonly IFileSystemWatcher _watcher;
private readonly object _loadingSettingsLock = new object();
private KeyboardListener _keyboardListener;
public SettingsService()
public SettingsService(KeyboardListener keyboardListener)
{
_settingsUtils = new SettingsUtils();
_keyboardListener = keyboardListener;
ReadSettings();
_watcher = Helper.GetFileWatcher(PowerAccentModuleName, "settings.json", () => { ReadSettings(); });
}
@@ -48,7 +51,10 @@ public class SettingsService
if (settings != null)
{
ActivationKey = settings.Properties.ActivationKey;
_keyboardListener.UpdateActivationKey((int)ActivationKey);
InputTime = settings.Properties.InputTime.Value;
_keyboardListener.UpdateInputTime(InputTime);
switch (settings.Properties.ToolbarPosition.Value)
{
case "Top center":
@@ -79,6 +85,8 @@ public class SettingsService
Position = Position.Center;
break;
}
_keyboardListener.UpdateInputTime(InputTime);
}
}
catch (Exception ex)
@@ -134,32 +142,27 @@ public class SettingsService
}
}
public char[] GetLetterKey(LetterKey letter)
{
return GetDefaultLetterKey(letter);
}
public static char[] GetDefaultLetterKey(LetterKey letter)
{
switch (letter)
{
case LetterKey.A:
case LetterKey.VK_A:
return new char[] { 'à', 'â', 'á', 'ä', 'ã', 'å', 'æ' };
case LetterKey.C:
case LetterKey.VK_C:
return new char[] { 'ć', 'ĉ', 'č', 'ċ', 'ç', 'ḉ' };
case LetterKey.E:
case LetterKey.VK_E:
return new char[] { 'é', 'è', 'ê', 'ë', 'ē', 'ė', '€' };
case LetterKey.I:
case LetterKey.VK_I:
return new char[] { 'î', 'ï', 'í', 'ì', 'ī' };
case LetterKey.N:
case LetterKey.VK_N:
return new char[] { 'ñ', 'ń' };
case LetterKey.O:
case LetterKey.VK_O:
return new char[] { 'ô', 'ö', 'ó', 'ò', 'õ', 'ø', 'œ' };
case LetterKey.S:
case LetterKey.VK_S:
return new char[] { 'š', 'ß', 'ś' };
case LetterKey.U:
case LetterKey.VK_U:
return new char[] { 'û', 'ù', 'ü', 'ú', 'ū' };
case LetterKey.Y:
case LetterKey.VK_Y:
return new char[] { 'ÿ', 'ý' };
}

View File

@@ -1,359 +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.
#pragma warning disable SA1310 // FieldNamesMustNotContainUnderscore
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace PowerAccent.Core.Tools;
internal class KeyboardListener : IDisposable
{
/// <summary>
/// Initializes a new instance of the <see cref="KeyboardListener"/> class.
/// Creates global keyboard listener.
/// </summary>
public KeyboardListener()
{
// We have to store the LowLevelKeyboardProc, so that it is not garbage collected by runtime
_hookedLowLevelKeyboardProc = LowLevelKeyboardProc;
// Set the hook
_hookId = InterceptKeys.SetHook(_hookedLowLevelKeyboardProc);
// Assign the asynchronous callback event
hookedKeyboardCallbackAsync = new KeyboardCallbackAsync(KeyboardListener_KeyboardCallbackAsync);
}
/// <summary>
/// Fired when any of the keys is pressed down.
/// </summary>
public event RawKeyEventHandler KeyDown;
/// <summary>
/// Fired when any of the keys is released.
/// </summary>
public event RawKeyEventHandler KeyUp;
/// <summary>
/// Hook ID
/// </summary>
private readonly IntPtr _hookId = IntPtr.Zero;
/// <summary>
/// Contains the hooked callback in runtime.
/// </summary>
private readonly InterceptKeys.LowLevelKeyboardProc _hookedLowLevelKeyboardProc;
/// <summary>
/// Event to be invoked asynchronously (BeginInvoke) each time key is pressed.
/// </summary>
private KeyboardCallbackAsync hookedKeyboardCallbackAsync;
/// <summary>
/// Raw keyevent handler.
/// </summary>
/// <param name="sender">sender</param>
/// <param name="args">raw keyevent arguments</param>
public delegate bool RawKeyEventHandler(object sender, RawKeyEventArgs args);
/// <summary>
/// Asynchronous callback hook.
/// </summary>
/// <param name="keyEvent">Keyboard event</param>
/// <param name="vkCode">VKCode</param>
/// <param name="character">Character</param>
private delegate bool KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode, string character);
/// <summary>
/// Actual callback hook.
/// <remarks>Calls asynchronously the asyncCallback.</remarks>
/// </summary>
/// <param name="nCode">VKCode</param>
/// <param name="wParam">wParam</param>
/// <param name="lParam">lParam</param>
[MethodImpl(MethodImplOptions.NoInlining)]
private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
if (wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYDOWN ||
wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYUP)
{
// Captures the character(s) pressed only on WM_KEYDOWN
var chars = InterceptKeys.VKCodeToString(
(uint)Marshal.ReadInt32(lParam),
wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYDOWN);
if (!hookedKeyboardCallbackAsync.Invoke((InterceptKeys.KeyEvent)wParam.ToUInt32(), Marshal.ReadInt32(lParam), chars))
{
return (IntPtr)1;
}
}
}
return InterceptKeys.CallNextHookEx(_hookId, nCode, wParam, lParam);
}
/// <summary>
/// HookCallbackAsync procedure that calls accordingly the KeyDown or KeyUp events.
/// </summary>
/// <param name="keyEvent">Keyboard event</param>
/// <param name="vkCode">VKCode</param>
/// <param name="character">Character as string.</param>
private bool KeyboardListener_KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode, string character)
{
switch (keyEvent)
{
// KeyDown events
case InterceptKeys.KeyEvent.WM_KEYDOWN:
if (KeyDown != null)
{
return KeyDown.Invoke(this, new RawKeyEventArgs(vkCode, character));
}
break;
// KeyUp events
case InterceptKeys.KeyEvent.WM_KEYUP:
if (KeyUp != null)
{
return KeyUp.Invoke(this, new RawKeyEventArgs(vkCode, character));
}
break;
default:
break;
}
return true;
}
public void Dispose()
{
InterceptKeys.UnhookWindowsHookEx(_hookId);
}
/// <summary>
/// Raw KeyEvent arguments.
/// </summary>
public class RawKeyEventArgs : EventArgs
{
/// <summary>
/// WPF Key of the key.
/// </summary>
#pragma warning disable SA1401 // Fields should be private
public uint Key;
#pragma warning restore SA1401 // Fields should be private
/// <summary>
/// Convert to string.
/// </summary>
/// <returns>Returns string representation of this key, if not possible empty string is returned.</returns>
public override string ToString()
{
return character;
}
/// <summary>
/// Unicode character of key pressed.
/// </summary>
private string character;
/// <summary>
/// Initializes a new instance of the <see cref="RawKeyEventArgs"/> class.
/// Create raw keyevent arguments.
/// </summary>
/// <param name="vKCode">VKCode</param>
/// <param name="character">Character</param>
public RawKeyEventArgs(int vKCode, string character)
{
this.character = character;
Key = (uint)vKCode; // User32.MapVirtualKey((uint)VKCode, User32.MAPVK.MAPVK_VK_TO_VSC_EX);
}
}
}
/// <summary>
/// Winapi Key interception helper class.
/// </summary>
internal static class InterceptKeys
{
public delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam);
private const int WH_KEYBOARD_LL = 13;
/// <summary>
/// Key event
/// </summary>
public enum KeyEvent : int
{
/// <summary>
/// Key down
/// </summary>
WM_KEYDOWN = 256,
/// <summary>
/// Key up
/// </summary>
WM_KEYUP = 257,
/// <summary>
/// System key up
/// </summary>
WM_SYSKEYUP = 261,
/// <summary>
/// System key down
/// </summary>
WM_SYSKEYDOWN = 260,
}
public static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc, (IntPtr)0, 0);
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
// Note: Sometimes single VKCode represents multiple chars, thus string.
// E.g. typing "^1" (notice that when pressing 1 the both characters appear,
// because of this behavior, "^" is called dead key)
[DllImport("user32.dll")]
#pragma warning disable CA1838 // Éviter les paramètres 'StringBuilder' pour les P/Invoke
private static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
#pragma warning restore CA1838 // Éviter les paramètres 'StringBuilder' pour les P/Invoke
[DllImport("user32.dll")]
private static extern bool GetKeyboardState(byte[] lpKeyState);
[DllImport("user32.dll")]
private static extern uint MapVirtualKeyEx(uint uCode, uint uMapType, IntPtr dwhkl);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetKeyboardLayout(uint dwLayout);
[DllImport("User32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("User32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("kernel32.dll")]
private static extern uint GetCurrentThreadId();
private static uint lastVKCode;
private static uint lastScanCode;
private static byte[] lastKeyState = new byte[255];
/// <summary>
/// Convert VKCode to Unicode.
/// <remarks>isKeyDown is required for because of keyboard state inconsistencies!</remarks>
/// </summary>
/// <param name="vKCode">VKCode</param>
/// <param name="isKeyDown">Is the key down event?</param>
/// <returns>String representing single unicode character.</returns>
public static string VKCodeToString(uint vKCode, bool isKeyDown)
{
// ToUnicodeEx needs StringBuilder, it populates that during execution.
System.Text.StringBuilder sbString = new System.Text.StringBuilder(5);
byte[] bKeyState = new byte[255];
bool bKeyStateStatus;
// Gets the current windows window handle, threadID, processID
IntPtr currentHWnd = GetForegroundWindow();
uint currentProcessID;
uint currentWindowThreadID = GetWindowThreadProcessId(currentHWnd, out currentProcessID);
// This programs Thread ID
uint thisProgramThreadId = GetCurrentThreadId();
// Attach to active thread so we can get that keyboard state
if (AttachThreadInput(thisProgramThreadId, currentWindowThreadID, true))
{
// Current state of the modifiers in keyboard
bKeyStateStatus = GetKeyboardState(bKeyState);
// Detach
AttachThreadInput(thisProgramThreadId, currentWindowThreadID, false);
}
else
{
// Could not attach, perhaps it is this process?
bKeyStateStatus = GetKeyboardState(bKeyState);
}
// On failure we return empty string.
if (!bKeyStateStatus)
{
return string.Empty;
}
// Gets the layout of keyboard
IntPtr hkl = GetKeyboardLayout(currentWindowThreadID);
// Maps the virtual keycode
uint lScanCode = MapVirtualKeyEx(vKCode, 0, hkl);
// Keyboard state goes inconsistent if this is not in place. In other words, we need to call above commands in UP events also.
if (!isKeyDown)
{
return string.Empty;
}
// Converts the VKCode to unicode
const uint wFlags = 1 << 2; // If bit 2 is set, keyboard state is not changed (Windows 10, version 1607 and newer)
int relevantKeyCountInBuffer = ToUnicodeEx(vKCode, lScanCode, bKeyState, sbString, sbString.Capacity, wFlags, hkl);
string ret = string.Empty;
switch (relevantKeyCountInBuffer)
{
// dead key
case -1:
break;
case 0:
break;
// Single character in buffer
case 1:
ret = sbString.Length == 0 ? string.Empty : sbString[0].ToString();
break;
// Two or more (only two of them is relevant)
case 2:
default:
ret = sbString.ToString().Substring(0, 2);
break;
}
// Save these
lastScanCode = lScanCode;
lastVKCode = vKCode;
lastKeyState = (byte[])bKeyState.Clone();
return ret;
}
}

View File

@@ -27,9 +27,10 @@ internal static class WindowsFunctions
}
// Letter
var inputsInsert = new User32.INPUT[1]
var inputsInsert = new User32.INPUT[]
{
new User32.INPUT { type = User32.INPUTTYPE.INPUT_KEYBOARD, ki = new User32.KEYBDINPUT { wVk = 0, dwFlags = User32.KEYEVENTF.KEYEVENTF_UNICODE, wScan = c } },
new User32.INPUT { type = User32.INPUTTYPE.INPUT_KEYBOARD, ki = new User32.KEYBDINPUT { wVk = 0, dwFlags = User32.KEYEVENTF.KEYEVENTF_UNICODE | User32.KEYEVENTF.KEYEVENTF_KEYUP, wScan = c } },
};
var temp2 = User32.SendInput((uint)inputsInsert.Length, inputsInsert, sizeof(User32.INPUT));
}

View File

@@ -2,7 +2,7 @@
<Import Project="..\..\..\Version.props" />
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
<Nullable>disable</Nullable>
<UseWPF>true</UseWPF>
@@ -26,10 +26,11 @@
<ItemGroup>
<PackageReference Include="gong-wpf-dragdrop" Version="3.1.1" />
<PackageReference Include="MahApps.Metro" Version="2.4.9" />
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PowerAccent.Core\PowerAccent.Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
<Import Project="..\..\..\Version.props" />
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
<UseWPF>true</UseWPF>
<Nullable>disable</Nullable>
@@ -12,11 +12,14 @@
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<AssemblyName>PowerToys.PowerAccent</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
<ProjectReference Include="..\PowerAccent.UI\PowerAccent.UI.csproj" />
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
<ProjectReference Include="..\PowerAccent.UI\PowerAccent.UI.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,207 @@
#include "pch.h"
#include "KeyboardListener.h"
#include "KeyboardListener.g.cpp"
#include <common/logger/logger.h>
#include <common/utils/logger_helper.h>
#include <common/utils/winapi_error.h>
namespace winrt::PowerToys::PowerAccentKeyboardService::implementation
{
KeyboardListener::KeyboardListener() :
m_toolbarVisible(false), m_triggeredWithSpace(false)
{
s_instance = this;
LoggerHelpers::init_logger(L"PowerAccent", L"PowerAccentKeyboardService", "PowerAccent");
}
void KeyboardListener::InitHook()
{
#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED)
const bool hook_disabled = IsDebuggerPresent();
#else
const bool hook_disabled = false;
#endif
if (!hook_disabled)
{
s_llKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), NULL);
if (!s_llKeyboardHook)
{
DWORD errorCode = GetLastError();
show_last_error_message(L"SetWindowsHookEx", errorCode, L"PowerToys - PowerAccent");
auto errorMessage = get_last_error_message(errorCode);
Logger::error(errorMessage.has_value() ? errorMessage.value() : L"");
}
}
}
void KeyboardListener::UnInitHook()
{
if (s_llKeyboardHook)
{
if (UnhookWindowsHookEx(s_llKeyboardHook))
{
s_llKeyboardHook = nullptr;
}
}
}
void KeyboardListener::SetShowToolbarEvent(ShowToolbar showToolbarEvent)
{
m_showToolbarCb = [trigger = std::move(showToolbarEvent)](LetterKey key) {
trigger(key);
};
}
void KeyboardListener::SetHideToolbarEvent(HideToolbar hideToolbarEvent)
{
m_hideToolbarCb = [trigger = std::move(hideToolbarEvent)](InputType inputType) {
trigger(inputType);
};
}
void KeyboardListener::SetNextCharEvent(NextChar nextCharEvent)
{
m_nextCharCb = [trigger = std::move(nextCharEvent)](TriggerKey triggerKey) {
trigger(triggerKey);
};
}
void KeyboardListener::UpdateActivationKey(int32_t activationKey)
{
m_settings.activationKey = static_cast<PowerAccentActivationKey>(activationKey);
}
void KeyboardListener::UpdateInputTime(int32_t inputTime)
{
m_settings.inputTime = std::chrono::milliseconds(inputTime);
}
bool KeyboardListener::OnKeyDown(KBDLLHOOKSTRUCT info) noexcept
{
if (std::find(std::begin(letters), end(letters), static_cast<LetterKey>(info.vkCode)) != end(letters))
{
m_stopwatch.reset();
letterPressed = static_cast<LetterKey>(info.vkCode);
}
UINT triggerPressed = 0;
if (letterPressed != LetterKey::None)
{
if (std::find(std::begin(triggers), end(triggers), static_cast<TriggerKey>(info.vkCode)) != end(triggers))
{
triggerPressed = info.vkCode;
if ((triggerPressed == VK_SPACE && m_settings.activationKey == PowerAccentActivationKey::LeftRightArrow) ||
((triggerPressed == VK_LEFT || triggerPressed == VK_RIGHT) && m_settings.activationKey == PowerAccentActivationKey::Space))
{
triggerPressed = 0;
Logger::info(L"Reset trigger key");
}
}
}
if (!m_toolbarVisible && letterPressed != LetterKey::None && triggerPressed)
{
Logger::info(L"Show toolbar. Letter: %d, Trigger: %d", letterPressed, triggerPressed);
// Keep track if it was triggered with space so that it can be typed on false starts.
m_triggeredWithSpace = triggerPressed == VK_SPACE;
m_toolbarVisible = true;
m_showToolbarCb(letterPressed);
}
if (m_toolbarVisible && triggerPressed)
{
if (triggerPressed == VK_LEFT)
{
Logger::info(L"Next toolbar position - left");
m_nextCharCb(TriggerKey::Left);
}
else if (triggerPressed == VK_RIGHT)
{
Logger::info(L"Next toolbar position - right");
m_nextCharCb(TriggerKey::Right);
}
else if (triggerPressed == VK_SPACE)
{
Logger::info(L"Next toolbar position - space");
m_nextCharCb(TriggerKey::Space);
}
return true;
}
return false;
}
bool KeyboardListener::OnKeyUp(KBDLLHOOKSTRUCT info) noexcept
{
if (std::find(std::begin(letters), end(letters), static_cast<LetterKey>(info.vkCode)) != end(letters))
{
letterPressed = LetterKey::None;
if (m_toolbarVisible)
{
if (m_stopwatch.elapsed() < m_settings.inputTime)
{
Logger::info(L"Activation too fast. Do nothing.");
// False start, we should output the space if it was the trigger.
if (m_triggeredWithSpace)
{
m_hideToolbarCb(InputType::Space);
}
else
{
m_hideToolbarCb(InputType::None);
}
m_toolbarVisible = false;
return true;
}
Logger::info(L"Hide toolbar event and input char");
m_hideToolbarCb(InputType::Char);
m_toolbarVisible = false;
}
}
return false;
}
LRESULT KeyboardListener::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
{
if (nCode == HC_ACTION && s_instance != nullptr)
{
KBDLLHOOKSTRUCT* key = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
switch (wParam)
{
case WM_KEYDOWN:
{
if (s_instance->OnKeyDown(*key))
{
return true;
}
}
break;
case WM_KEYUP:
{
if (s_instance->OnKeyUp(*key))
{
return true;
}
}
break;
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
}
}

View File

@@ -0,0 +1,75 @@
#pragma once
#include "KeyboardListener.g.h"
#include <spdlog/stopwatch.h>
namespace winrt::PowerToys::PowerAccentKeyboardService::implementation
{
enum PowerAccentActivationKey
{
LeftRightArrow,
Space,
Both,
};
struct PowerAccentSettings
{
PowerAccentActivationKey activationKey{ PowerAccentActivationKey::Both };
std::chrono::milliseconds inputTime{ 200 };
};
struct KeyboardListener : KeyboardListenerT<KeyboardListener>
{
using LetterKey = winrt::PowerToys::PowerAccentKeyboardService::LetterKey;
using TriggerKey = winrt::PowerToys::PowerAccentKeyboardService::TriggerKey;
using InputType = winrt::PowerToys::PowerAccentKeyboardService::InputType;
KeyboardListener();
void KeyboardListener::InitHook();
void KeyboardListener::UnInitHook();
void SetShowToolbarEvent(ShowToolbar showToolbarEvent);
void SetHideToolbarEvent(HideToolbar hideToolbarEvent);
void SetNextCharEvent(NextChar NextCharEvent);
void UpdateActivationKey(int32_t activationKey);
void UpdateInputTime(int32_t inputTime);
static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
private:
bool OnKeyDown(KBDLLHOOKSTRUCT info) noexcept;
bool OnKeyUp(KBDLLHOOKSTRUCT info) noexcept;
static inline KeyboardListener* s_instance;
HHOOK s_llKeyboardHook = nullptr;
bool m_toolbarVisible;
PowerAccentSettings m_settings;
std::function<void(LetterKey)> m_showToolbarCb;
std::function<void(InputType)> m_hideToolbarCb;
std::function<void(TriggerKey)> m_nextCharCb;
bool m_triggeredWithSpace;
spdlog::stopwatch m_stopwatch;
static inline const std::vector<LetterKey> letters = { LetterKey::VK_A,
LetterKey::VK_C,
LetterKey::VK_E,
LetterKey::VK_I,
LetterKey::VK_N,
LetterKey::VK_O,
LetterKey::VK_S,
LetterKey::VK_U,
LetterKey::VK_Y };
LetterKey letterPressed{};
static inline const std::vector<TriggerKey> triggers = { TriggerKey::Right, TriggerKey::Left, TriggerKey::Space };
};
}
namespace winrt::PowerToys::PowerAccentKeyboardService::factory_implementation
{
struct KeyboardListener : KeyboardListenerT<KeyboardListener, implementation::KeyboardListener>
{
};
}

View File

@@ -0,0 +1,48 @@
namespace PowerToys
{
namespace PowerAccentKeyboardService
{
enum LetterKey
{
None = 0x00,
VK_A = 0x41,
VK_C = 0x43,
VK_E = 0x45,
VK_I = 0x49,
VK_N = 0x4E,
VK_O = 0x4F,
VK_S = 0x53,
VK_U = 0x55,
VK_Y = 0x59
};
enum TriggerKey
{
Right = 0x27, // VK_RIGHT
Left = 0x25, // VK_LEFT
Space = 0x20 // VK_SPACE
};
enum InputType
{
None,
Space,
Char
};
[version(1.0), uuid(37197089-5438-4479-af57-30ab3f3c8be4)] delegate void ShowToolbar(LetterKey key);
[version(1.0), uuid(8eb79d6b-1826-424f-9fbc-af21ae19725e)] delegate void HideToolbar(InputType inputType);
[version(1.0), uuid(db72d45c-a5a2-446f-bdc1-506e9121764a)] delegate void NextChar(TriggerKey inputSpace);
[default_interface] runtimeclass KeyboardListener {
KeyboardListener();
void InitHook();
void UnInitHook();
void SetShowToolbarEvent(event ShowToolbar showToolbarEvent);
void SetHideToolbarEvent(event HideToolbar hideToolbarEvent);
void SetNextCharEvent(event NextChar nextCharEvent);
void UpdateActivationKey(Int32 activationKey);
void UpdateInputTime(Int32 inputTime);
}
}
}

View File

@@ -0,0 +1,3 @@
EXPORTS
DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE
DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE

View File

@@ -0,0 +1,40 @@
#include <windows.h>
#include "resource.h"
#include "../../../common/version/version.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#undef APSTUDIO_READONLY_SYMBOLS
1 VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
END
END

View File

@@ -0,0 +1,139 @@
<?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.220418.1\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220418.1\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
<CppWinRTOptimized>true</CppWinRTOptimized>
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
<CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata>
<MinimalCoreWin>true</MinimalCoreWin>
<ProjectGuid>{c97d9a5d-206c-454e-997e-009e227d7f02}</ProjectGuid>
<ProjectName>PowerAccentKeyboardService</ProjectName>
<RootNamespace>PowerToys.PowerAccentKeyboardService</RootNamespace>
<DefaultLanguage>en-US</DefaultLanguage>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
<AppContainerApplication>false</AppContainerApplication>
<AppxPackage>false</AppxPackage>
<ApplicationType>Windows Store</ApplicationType>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.19041.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="PropertySheet.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<TargetName>PowerToys.PowerAccentKeyboardService</TargetName>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\PowerAccent\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions>
<!--Temporarily disable cppwinrt heap enforcement to work around xaml compiler generated std::shared_ptr use -->
<AdditionalOptions Condition="'$(CppWinRTHeapEnforcement)'==''">/DWINRT_NO_MAKE_DETECTION %(AdditionalOptions)</AdditionalOptions>
<PreprocessorDefinitions>_WINRT_DLL;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../../..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
<ModuleDefinitionFile>PowerAccentKeyboardService.def</ModuleDefinitionFile>
<AdditionalDependencies>Shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="KeyboardListener.h">
<DependentUpon>KeyboardListener.idl</DependentUpon>
</ClInclude>
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="KeyboardListener.cpp">
<DependentUpon>KeyboardListener.idl</DependentUpon>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
</ItemGroup>
<ItemGroup>
<Midl Include="KeyboardListener.idl" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="PowerAccentKeyboardService.def" />
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\version\version.vcxproj">
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="PowerAccentKeyboardService.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220418.1\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220418.1\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.220418.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220418.1\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220418.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220418.1\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Resources">
<UniqueIdentifier>accd3aa8-1ba0-4223-9bbe-0c431709210b</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Generated Files">
<UniqueIdentifier>{926ab91d-31b4-48c3-b9a4-e681349f27f0}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp" />
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<Midl Include="KeyboardListener.idl" />
</ItemGroup>
<ItemGroup>
<None Include="PowerAccentKeyboardService.def" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="PowerAccentKeyboardService.rc">
<Filter>Resources</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<!--
To customize common C++/WinRT project properties:
* right-click the project node
* expand the Common Properties item
* select the C++/WinRT property page
For more advanced scenarios, and complete documentation, please see:
https://github.com/Microsoft/cppwinrt/tree/master/nuget
-->
<PropertyGroup />
<ItemDefinitionGroup />
</Project>

View File

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

View File

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

View File

@@ -0,0 +1,4 @@
#pragma once
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>

View File

@@ -0,0 +1,13 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by PowerToys.MeasureToolCore.rc
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys PowerAccentKeyboardService"
#define INTERNAL_NAME "PowerToys.PowerAccentKeyboardService"
#define ORIGINAL_FILENAME "PowerToys.PowerAccentKeyboardService.dll"
// Non-localizable
//////////////////////////////

View File

@@ -25,7 +25,7 @@
<PropertyGroup>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\PowerAccent\</OutDir>
</PropertyGroup>
<PropertyGroup >
<PropertyGroup>
<TargetName>PowerToys.$(ProjectName)</TargetName>
</PropertyGroup>
<ItemDefinitionGroup>

View File

@@ -307,7 +307,6 @@ int wmain(int argc, wchar_t* argv[], wchar_t*)
if (!DeleteFolder(reportDir))
{
printf("Failed to delete temp folder\n");
return 1;
}
try
@@ -332,7 +331,6 @@ int wmain(int argc, wchar_t* argv[], wchar_t*)
catch (...)
{
printf("Failed to copy logs saved in LocalLow\n");
return 1;
}
#ifndef _DEBUG