[PowerAccent] Move low-level keyboard hook to c++ (#20190)

* Move llkeyboardhook to c++

* expect.txt

* Address PR comment - Resolve namespaces

* Address PR comments - CallNextHook and correct char selection

* Also unpress the letter key

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
This commit is contained in:
Stefan Markovic 2022-09-09 13:27:56 +02:00 committed by GitHub
parent 7f8c5c9f0c
commit 7b0f97597d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 760 additions and 548 deletions

View File

@ -742,7 +742,7 @@ HKCC
HKCR HKCR
HKCU HKCU
hkey hkey
hkl HKL
HKLM HKLM
HKPD HKPD
HKU HKU

View File

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

View File

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

View File

@ -125,7 +125,7 @@
<?define MeasureToolMicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?> <?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="*" <Product Id="*"
Name="PowerToys (Preview)" Name="PowerToys (Preview)"

View File

@ -1,27 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Vanara.PInvoke;
namespace PowerAccent.Core;
public enum LetterKey
{
A = User32.VK.VK_A,
C = User32.VK.VK_C,
E = User32.VK.VK_E,
I = User32.VK.VK_I,
N = User32.VK.VK_N,
O = User32.VK.VK_O,
S = User32.VK.VK_S,
U = User32.VK.VK_U,
Y = User32.VK.VK_Y,
}
public enum TriggerKey
{
Left = User32.VK.VK_LEFT,
Right = User32.VK.VK_RIGHT,
Space = User32.VK.VK_SPACE,
}

View File

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

View File

@ -2,64 +2,67 @@
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System.Diagnostics; using System.Windows;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using PowerAccent.Core.Services; using PowerAccent.Core.Services;
using PowerAccent.Core.Tools; using PowerAccent.Core.Tools;
using PowerToys.PowerAccentKeyboardService;
namespace PowerAccent.Core; namespace PowerAccent.Core;
public class PowerAccent : IDisposable public class PowerAccent : IDisposable
{ {
private readonly SettingsService _settingService = new SettingsService(); private readonly SettingsService _settingService;
private readonly KeyboardListener _keyboardListener = new KeyboardListener();
private LetterKey? letterPressed;
private bool _visible; private bool _visible;
private char[] _characters = Array.Empty<char>(); private char[] _characters = Array.Empty<char>();
private int _selectedIndex = -1; private int _selectedIndex = -1;
private Stopwatch _stopWatch;
private bool _triggeredWithSpace;
public event Action<bool, char[]> OnChangeDisplay; public event Action<bool, char[]> OnChangeDisplay;
public event Action<int, char> OnSelectCharacter; public event Action<int, char> OnSelectCharacter;
private KeyboardListener _keyboardListener;
public PowerAccent() public PowerAccent()
{ {
_keyboardListener.KeyDown += PowerAccent_KeyDown; _keyboardListener = new KeyboardListener();
_keyboardListener.KeyUp += PowerAccent_KeyUp; _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(); Application.Current.Dispatcher.Invoke(() =>
letterPressed = (LetterKey)args.Key; {
ShowToolbar(letterKey);
});
}));
_keyboardListener.SetHideToolbarEvent(new PowerToys.PowerAccentKeyboardService.HideToolbar((InputType inputType) =>
{
Application.Current.Dispatcher.Invoke(() =>
{
SendInputAndHideToolbar(inputType);
});
}));
_keyboardListener.SetNextCharEvent(new PowerToys.PowerAccentKeyboardService.NextChar((TriggerKey triggerKey) =>
{
Application.Current.Dispatcher.Invoke(() =>
{
ProcessNextChar(triggerKey);
});
}));
} }
TriggerKey? triggerPressed = null; private void ShowToolbar(LetterKey letterKey)
if (letterPressed.HasValue)
{ {
if (Enum.IsDefined(typeof(TriggerKey), (int)args.Key))
{
triggerPressed = (TriggerKey)args.Key;
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)
{
// Keep track if it was triggered with space so that it can be typed on false starts.
_triggeredWithSpace = triggerPressed.Value == TriggerKey.Space;
_visible = true; _visible = true;
_characters = WindowsFunctions.IsCapitalState() ? ToUpper(_settingService.GetLetterKey(letterPressed.Value)) : _settingService.GetLetterKey(letterPressed.Value); _characters = WindowsFunctions.IsCapitalState() ? ToUpper(SettingsService.GetDefaultLetterKey(letterKey)) : SettingsService.GetDefaultLetterKey(letterKey);
Task.Delay(_settingService.InputTime).ContinueWith( Task.Delay(_settingService.InputTime).ContinueWith(
t => t =>
{ {
@ -70,21 +73,47 @@ public class PowerAccent : IDisposable
}, TaskScheduler.FromCurrentSynchronizationContext()); }, TaskScheduler.FromCurrentSynchronizationContext());
} }
if (_visible && triggerPressed.HasValue) private void SendInputAndHideToolbar(InputType inputType)
{ {
if (_selectedIndex == -1) switch (inputType)
{ {
if (triggerPressed.Value == TriggerKey.Left) case InputType.Space:
{
WindowsFunctions.Insert(' ');
break;
}
case InputType.Char:
{
if (_selectedIndex != -1)
{
WindowsFunctions.Insert(_characters[_selectedIndex], true);
}
break;
}
}
OnChangeDisplay?.Invoke(false, null);
_selectedIndex = -1;
_visible = false;
}
private void ProcessNextChar(TriggerKey triggerKey)
{
if (_visible && _selectedIndex == -1)
{
if (triggerKey == TriggerKey.Left)
{ {
_selectedIndex = (_characters.Length / 2) - 1; _selectedIndex = (_characters.Length / 2) - 1;
} }
if (triggerPressed.Value == TriggerKey.Right) if (triggerKey == TriggerKey.Right)
{ {
_selectedIndex = _characters.Length / 2; _selectedIndex = _characters.Length / 2;
} }
if (triggerPressed.Value == TriggerKey.Space) if (triggerKey == TriggerKey.Space)
{ {
_selectedIndex = 0; _selectedIndex = 0;
} }
@ -100,10 +129,10 @@ public class PowerAccent : IDisposable
} }
OnSelectCharacter?.Invoke(_selectedIndex, _characters[_selectedIndex]); OnSelectCharacter?.Invoke(_selectedIndex, _characters[_selectedIndex]);
return false; return;
} }
if (triggerPressed.Value == TriggerKey.Space) if (triggerKey == TriggerKey.Space)
{ {
if (_selectedIndex < _characters.Length - 1) if (_selectedIndex < _characters.Length - 1)
{ {
@ -115,60 +144,17 @@ public class PowerAccent : IDisposable
} }
} }
if (triggerPressed.Value == TriggerKey.Left && _selectedIndex > 0) if (triggerKey == TriggerKey.Left && _selectedIndex > 0)
{ {
--_selectedIndex; --_selectedIndex;
} }
if (triggerPressed.Value == TriggerKey.Right && _selectedIndex < _characters.Length - 1) if (triggerKey == TriggerKey.Right && _selectedIndex < _characters.Length - 1)
{ {
++_selectedIndex; ++_selectedIndex;
} }
OnSelectCharacter?.Invoke(_selectedIndex, _characters[_selectedIndex]); OnSelectCharacter?.Invoke(_selectedIndex, _characters[_selectedIndex]);
return false;
}
return true;
}
private bool PowerAccent_KeyUp(object sender, KeyboardListener.RawKeyEventArgs args)
{
if (Enum.IsDefined(typeof(LetterKey), (int)args.Key))
{
letterPressed = null;
_stopWatch.Stop();
if (_visible)
{
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;
}
}
return true;
} }
public Point GetDisplayCoordinates(Size window) public Point GetDisplayCoordinates(Size window)
@ -182,14 +168,9 @@ public class PowerAccent : IDisposable
return Calculation.GetRawCoordinatesFromPosition(position, screen, window); return Calculation.GetRawCoordinatesFromPosition(position, screen, window);
} }
public char[] GetLettersFromKey(LetterKey letter)
{
return _settingService.GetLetterKey(letter);
}
public void Dispose() public void Dispose()
{ {
_keyboardListener.Dispose(); _keyboardListener.UnInitHook();
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }

View File

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

View File

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

View File

@ -27,9 +27,10 @@ internal static class WindowsFunctions
} }
// Letter // 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, 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)); var temp2 = User32.SendInput((uint)inputsInsert.Length, inputsInsert, sizeof(User32.INPUT));
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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