mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-29 16:36:40 +01:00
Compare commits
5 Commits
copilot/fi
...
v0.62.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18fc38115b | ||
|
|
9d8bfead0c | ||
|
|
8872cf651b | ||
|
|
702b69d5a4 | ||
|
|
bdc1889eba |
24
.github/actions/spell-check/expect.txt
vendored
24
.github/actions/spell-check/expect.txt
vendored
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)"
|
||||
|
||||
12
installer/PowerToysSetup/terminate_powertoys.cmd
Normal file
12
installer/PowerToysSetup/terminate_powertoys.cmd
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
23
src/modules/awake/Awake/NativeMethods.txt
Normal file
23
src/modules/awake/Awake/NativeMethods.txt
Normal 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
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
using System;
|
||||
using ColorPicker.Helpers;
|
||||
using ColorPicker.Mouse;
|
||||
|
||||
using ColorPickerUI;
|
||||
|
||||
namespace ColorPicker
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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[] { 'ÿ', 'ý' };
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
{
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
EXPORTS
|
||||
DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE
|
||||
DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
||||
4
src/modules/poweraccent/PowerAccentKeyboardService/pch.h
Normal file
4
src/modules/poweraccent/PowerAccentKeyboardService/pch.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <unknwn.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
@@ -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
|
||||
//////////////////////////////
|
||||
@@ -25,7 +25,7 @@
|
||||
<PropertyGroup>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\PowerAccent\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup >
|
||||
<PropertyGroup>
|
||||
<TargetName>PowerToys.$(ProjectName)</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user