mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 02:06:36 +02:00
[KBM] Migrate Engine and Editor into separate processes (#10774)
* Move KBM engine into separate process (#10672) * [KBM] Migrate KBM UI out of the runner (#10709) * Clean up keyboard hook handles (#10817) * [C++ common] Unhandled exception handler (#10821) * [KBM] Use icon in the KeyboardManagerEditor (#10845) * [KBM] Move resources from the Common project to the Editor. (#10844) * KBM Editor tests (#10858) * Rename engine executable (#10868) * clean up (#10870) * [KBM] Changed Editor and libraries output folders (#10871) * [KBM] New logs structure (#10872) * Add unhandled exception handling to the editor (#10874) * [KBM] Trace for edit keyboard window * Logging for XamlBridge message loop * [KBM] Added Editor and Engine to the installer (#10876) * Fix spelling * Interprocess communication logs, remove unnecessary windows message logs * [KBM] Separated telemetry for the engine and editor. (#10889) * [KBM] Editor test project (#10891) * Versions for the engine and the editor (#10897) * Add the editor's and the engine's executables to signing process (#10900) * [KBM editor] Run only one instance, exit when parent process exits (#10890) * [KBM] Force kill editor process to avoid XAML crash (#10907) * [KBM] Force kill editor process to avoid XAML crash * Fix event releasing Co-authored-by: mykhailopylyp <17161067+mykhailopylyp@users.noreply.github.com> * Make the editor dpi aware (#10908) * [KBM] KeyboardManagerCommon refactoring (#10909) * Do not start the process if it is already started (#10910) * logs * Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp * Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp * [KBM] Rename InitUnhandledExceptionHandler to make it explicit that is for x64 only. We will fix it properly when adding support for ARM64 and add a header with the proper conditional building. * [KBM] rename file/class/variables using camel case * [KBM] Rename "event_locker" -> "EventLocker" * [KBM] rename process_waiter Add a TODO comment * [KBM] rename methods Add TODO comment * [KBM] use uppercase for function names * [KBM] use uppercase for methos, lowercase for properties * [KBM] rename method, make methods private, formatting * [KBM] rename private variables * [KBM] use uppercase for function names * [KBM] Added support to run the editor stand-alone when built in debug mode * Update src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp * Check success of event creation, comment (#10947) * [KBM] code formatting (#10951) * [KBM] code formatting * Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp * [KBM] tracing * [KBM] Remappings not showing fix. (#10954) * removed mutex * retry loop for reading * retry on reading config once * log error Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com> Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com> Co-authored-by: Seraphima Zykova <zykovas91@gmail.com> Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com> Co-authored-by: Enrico Giordani <enrico.giordani@gmail.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,36 @@
|
||||
#include <windows.h>
|
||||
#include "resource.h"
|
||||
#include "../../../common/version/version.h"
|
||||
|
||||
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,83 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{62173D9A-6724-4C00-A1C8-FB646480A9EC}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>KeyboardManagerEditorTest</RootNamespace>
|
||||
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||
<ProjectSubType>NativeUnitTestProject</ProjectSubType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\KeyboardManagerEditor\</OutDir>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(VCInstallDir)UnitTest\include;$(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<UseFullPaths>true</UseFullPaths>
|
||||
<DisableSpecificWarnings>4002</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BufferValidationTests.cpp" />
|
||||
<ClCompile Include="LoadingAndSavingRemappingTests.cpp" />
|
||||
<ClCompile Include="MockedInput.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TestHelpers.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="MockedInput.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="TestHelpers.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SetttingsAPI.vcxproj">
|
||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\KeyboardManagerCommon.vcxproj">
|
||||
<Project>{8affa899-0b73-49ec-8c50-0fadda57b2fc}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\KeyboardManagerEditorLibrary\KeyboardManagerEditorLibrary.vcxproj">
|
||||
<Project>{23d2070d-e4ad-4add-85a7-083d9c76ad49}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="KeyboardManagerEditorTest.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LoadingAndSavingRemappingTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BufferValidationTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TestHelpers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MockedInput.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MockedInput.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TestHelpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="KeyboardManagerEditorTest.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,522 @@
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/KeyboardManagerEditorLibrary/LoadingAndSavingRemappingHelper.h>
|
||||
#include "TestHelpers.h"
|
||||
#include <common/interop/shared_constants.h>
|
||||
#include <keyboardmanager/common/ErrorTypes.h>
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace RemappingUITests
|
||||
{
|
||||
// Tests for methods in the LoadingAndSavingRemappingHelper namespace
|
||||
TEST_CLASS (LoadingAndSavingRemappingTests)
|
||||
{
|
||||
std::wstring testApp1 = L"testtrocess1.exe";
|
||||
std::wstring testApp2 = L"testprocess2.exe";
|
||||
|
||||
public:
|
||||
TEST_METHOD_INITIALIZE(InitializeTestEnv)
|
||||
{
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is successful when no remaps are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnNoError_OnPassingNoRemaps)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Assert that remapping set is valid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is successful when valid key to key remaps are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnNoError_OnPassingValidKeyToKeyRemaps)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to B and B to C
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, 0x42 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, 0x43 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is valid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is successful when valid key to shortcut remaps are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnNoError_OnPassingValidKeyToShortcutRemaps)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to Ctrl+V and B to Alt+Tab
|
||||
Shortcut s1;
|
||||
s1.SetKey(VK_CONTROL);
|
||||
s1.SetKey(0x56);
|
||||
Shortcut s2;
|
||||
s2.SetKey(VK_MENU);
|
||||
s2.SetKey(VK_TAB);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, s1 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, s2 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is valid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is successful when valid shortcut to key remaps are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnNoError_OnPassingValidShortcutToKeyRemaps)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap Ctrl+V to A and Alt+Tab to B
|
||||
Shortcut s1;
|
||||
s1.SetKey(VK_CONTROL);
|
||||
s1.SetKey(0x56);
|
||||
Shortcut s2;
|
||||
s2.SetKey(VK_MENU);
|
||||
s2.SetKey(VK_TAB);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ s1, 0x41 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ s2, 0x42 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is valid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is successful when valid shortcut to shortcut remaps are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnNoError_OnPassingValidShortcutToShortcutRemaps)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap Ctrl+V to Ctrl+D and Alt+Tab to Win+A
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x56);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x44);
|
||||
Shortcut src2;
|
||||
src2.SetKey(VK_MENU);
|
||||
src2.SetKey(VK_TAB);
|
||||
Shortcut dest2;
|
||||
dest2.SetKey(CommonSharedConstants::VK_WIN_BOTH);
|
||||
dest2.SetKey(0x41);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src2, dest2 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is valid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is successful when valid remaps are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnNoError_OnPassingValidRemapsOfAllTypes)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap Ctrl+V to Ctrl+D, Alt+Tab to A, A to B and B to Win+A
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x56);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x44);
|
||||
Shortcut src2;
|
||||
src2.SetKey(VK_MENU);
|
||||
src2.SetKey(VK_TAB);
|
||||
Shortcut dest2;
|
||||
dest2.SetKey(CommonSharedConstants::VK_WIN_BOTH);
|
||||
dest2.SetKey(0x41);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src2, 0x41 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, 0x42 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, dest2 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is valid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is unsuccessful when remaps with null keys are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnRemapUnsuccessful_OnPassingRemapsWithNullKeys)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to NULL
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, NULL }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is invalid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is unsuccessful when remaps with invalid shortcuts are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnRemapUnsuccessful_OnPassingRemapsWithInvalidShortcut)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to incomplete shortcut (Ctrl)
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, src1 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is invalid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is unsuccessful when remaps with the same key remapped twice are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnRemapUnsuccessful_OnPassingRemapsWithSameKeyRemappedTwice)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to B and A to Ctrl+C
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x43);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, 0x42 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, src1 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is invalid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is unsuccessful when remaps with the same shortcut remapped twice are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnRemapUnsuccessful_OnPassingRemapsWithSameShortcutRemappedTwice)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap Ctrl+A to B and Ctrl+A to Ctrl+V
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x41);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x56);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, 0x42 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), std::wstring()));
|
||||
|
||||
// Assert that remapping set is invalid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is unsuccessful when app specific remaps with the same shortcut remapped twice for the same target app are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnRemapUnsuccessful_OnPassingAppSpecificRemapsWithSameShortcutRemappedTwiceForTheSameTargetApp)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap Ctrl+A to B and Ctrl+A to Ctrl+V for testApp1
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x41);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x56);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, 0x42 }), testApp1));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), testApp1));
|
||||
|
||||
// Assert that remapping set is invalid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the CheckIfRemappingsAreValid method is successful when app specific remaps with the same shortcut remapped twice for different target apps are passed
|
||||
TEST_METHOD (CheckIfRemappingsAreValid_ShouldReturnNoError_OnPassingAppSpecificRemapsWithSameShortcutRemappedTwiceForDifferentTargetApps)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap Ctrl+A to B for testApp1 and Ctrl+A to Ctrl+V for testApp2
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x41);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x56);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, 0x42 }), testApp1));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), testApp2));
|
||||
|
||||
// Assert that remapping set is valid
|
||||
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError);
|
||||
Assert::AreEqual(true, isSuccess);
|
||||
}
|
||||
|
||||
// Test if the GetOrphanedKeys method return an empty vector on passing no remaps
|
||||
TEST_METHOD (GetOrphanedKeys_ShouldReturnEmptyVector_OnPassingNoRemaps)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Assert that there are no orphaned keys
|
||||
Assert::AreEqual(true, LoadingAndSavingRemappingHelper::GetOrphanedKeys(remapBuffer).empty());
|
||||
}
|
||||
|
||||
// Test if the GetOrphanedKeys method return one orphaned on passing one key remap
|
||||
TEST_METHOD (GetOrphanedKeys_ShouldReturnOneOrphanedKey_OnPassingOneKeyRemap)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to B
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, 0x42 }), std::wstring()));
|
||||
|
||||
// Assert that only A is orphaned
|
||||
Assert::AreEqual((size_t)1, LoadingAndSavingRemappingHelper::GetOrphanedKeys(remapBuffer).size());
|
||||
Assert::AreEqual((DWORD)0x41, LoadingAndSavingRemappingHelper::GetOrphanedKeys(remapBuffer)[0]);
|
||||
}
|
||||
|
||||
// Test if the GetOrphanedKeys method return an empty vector on passing swapped key remaps
|
||||
TEST_METHOD (GetOrphanedKeys_ShouldReturnEmptyVector_OnPassingSwappedKeyRemap)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to B and B to A
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, 0x42 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, 0x41 }), std::wstring()));
|
||||
|
||||
// Assert that there are no orphaned keys
|
||||
Assert::AreEqual(true, LoadingAndSavingRemappingHelper::GetOrphanedKeys(remapBuffer).empty());
|
||||
}
|
||||
|
||||
// Test if the GetOrphanedKeys method return one orphaned on passing two key remaps where one key is mapped to a remapped key
|
||||
TEST_METHOD (GetOrphanedKeys_ShouldReturnOneOrphanedKey_OnPassingTwoKeyRemapsWhereOneKeyIsMappedToARemappedKey)
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to Ctrl+B and C to A
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x42);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, dest1 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x43, 0x41 }), std::wstring()));
|
||||
|
||||
// Assert that only C is orphaned
|
||||
Assert::AreEqual((size_t)1, LoadingAndSavingRemappingHelper::GetOrphanedKeys(remapBuffer).size());
|
||||
Assert::AreEqual((DWORD)0x43, LoadingAndSavingRemappingHelper::GetOrphanedKeys(remapBuffer)[0]);
|
||||
}
|
||||
|
||||
// Test if the PreProcessRemapTable method combines all the modifier pairs when the left and right modifiers are remapped to the same target
|
||||
TEST_METHOD (PreProcessRemapTable_ShouldCombineAllPairs_OnPassingLeftAndRightModifiersRemappedToTheSameTarget)
|
||||
{
|
||||
SingleKeyRemapTable remapTable;
|
||||
|
||||
// Remap LCtrl and RCtrl to A, LAlt and RAlt to B, LShift and RShift to C, LWin and RWin to D
|
||||
remapTable[VK_LCONTROL] = 0x41;
|
||||
remapTable[VK_RCONTROL] = 0x41;
|
||||
remapTable[VK_LMENU] = 0x42;
|
||||
remapTable[VK_RMENU] = 0x42;
|
||||
remapTable[VK_LSHIFT] = 0x43;
|
||||
remapTable[VK_RSHIFT] = 0x43;
|
||||
remapTable[VK_LWIN] = 0x44;
|
||||
remapTable[VK_RWIN] = 0x44;
|
||||
|
||||
// Pre process table
|
||||
LoadingAndSavingRemappingHelper::PreProcessRemapTable(remapTable);
|
||||
|
||||
// Expected Ctrl remapped to A, Alt to B, Shift to C, Win to D
|
||||
SingleKeyRemapTable expectedTable;
|
||||
expectedTable[VK_CONTROL] = 0x41;
|
||||
expectedTable[VK_MENU] = 0x42;
|
||||
expectedTable[VK_SHIFT] = 0x43;
|
||||
expectedTable[CommonSharedConstants::VK_WIN_BOTH] = 0x44;
|
||||
|
||||
bool areTablesEqual = (expectedTable == remapTable);
|
||||
Assert::AreEqual(true, areTablesEqual);
|
||||
}
|
||||
|
||||
// Test if the PreProcessRemapTable method does not combines any of the modifier pairs when the left and right modifiers are remapped to different targets
|
||||
TEST_METHOD (PreProcessRemapTable_ShouldNotCombineAnyPairs_OnPassingLeftAndRightModifiersRemappedToTheDifferentTargets)
|
||||
{
|
||||
SingleKeyRemapTable remapTable;
|
||||
|
||||
// Remap left modifiers to A and right modifiers to B
|
||||
remapTable[VK_LCONTROL] = 0x41;
|
||||
remapTable[VK_RCONTROL] = 0x42;
|
||||
remapTable[VK_LMENU] = 0x41;
|
||||
remapTable[VK_RMENU] = 0x42;
|
||||
remapTable[VK_LSHIFT] = 0x41;
|
||||
remapTable[VK_RSHIFT] = 0x42;
|
||||
remapTable[VK_LWIN] = 0x41;
|
||||
remapTable[VK_RWIN] = 0x42;
|
||||
|
||||
// Pre process table
|
||||
LoadingAndSavingRemappingHelper::PreProcessRemapTable(remapTable);
|
||||
|
||||
// Expected unchanged table
|
||||
SingleKeyRemapTable expectedTable;
|
||||
expectedTable[VK_LCONTROL] = 0x41;
|
||||
expectedTable[VK_RCONTROL] = 0x42;
|
||||
expectedTable[VK_LMENU] = 0x41;
|
||||
expectedTable[VK_RMENU] = 0x42;
|
||||
expectedTable[VK_LSHIFT] = 0x41;
|
||||
expectedTable[VK_RSHIFT] = 0x42;
|
||||
expectedTable[VK_LWIN] = 0x41;
|
||||
expectedTable[VK_RWIN] = 0x42;
|
||||
|
||||
bool areTablesEqual = (expectedTable == remapTable);
|
||||
Assert::AreEqual(true, areTablesEqual);
|
||||
}
|
||||
|
||||
// Test if the ApplySingleKeyRemappings method resets the keyboard manager state's single key remappings on passing an empty buffer
|
||||
TEST_METHOD (ApplySingleKeyRemappings_ShouldResetSingleKeyRemappings_OnPassingEmptyBuffer)
|
||||
{
|
||||
KeyboardManagerState testState;
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap A to B
|
||||
testState.AddSingleKeyRemap(0x41, 0x42);
|
||||
|
||||
// Apply the single key remaps from the buffer to the keyboard manager state variable
|
||||
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(testState, remapBuffer, false);
|
||||
|
||||
// Assert that single key remapping in the kbm state variable is empty
|
||||
Assert::AreEqual((size_t)0, testState.singleKeyReMap.size());
|
||||
}
|
||||
|
||||
// Test if the ApplySingleKeyRemappings method copies only the valid remappings to the keyboard manager state variable when some of the remappings are invalid
|
||||
TEST_METHOD (ApplySingleKeyRemappings_ShouldCopyOnlyValidRemappings_OnPassingBufferWithSomeInvalidRemappings)
|
||||
{
|
||||
KeyboardManagerState testState;
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Add A->B, B->Ctrl+V, C to incomplete shortcut and D to incomplete key remappings to the buffer
|
||||
Shortcut s1;
|
||||
s1.SetKey(VK_CONTROL);
|
||||
s1.SetKey(0x56);
|
||||
Shortcut s2;
|
||||
s2.SetKey(VK_LMENU);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, 0x42 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, s1 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x43, NULL }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x44, s2 }), std::wstring()));
|
||||
|
||||
// Apply the single key remaps from the buffer to the keyboard manager state variable
|
||||
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(testState, remapBuffer, false);
|
||||
|
||||
// Expected A remapped to B, B remapped to Ctrl+V
|
||||
SingleKeyRemapTable expectedTable;
|
||||
expectedTable[0x41] = 0x42;
|
||||
expectedTable[0x42] = s1;
|
||||
|
||||
bool areTablesEqual = (expectedTable == testState.singleKeyReMap);
|
||||
Assert::AreEqual(true, areTablesEqual);
|
||||
}
|
||||
|
||||
// Test if the ApplySingleKeyRemappings method splits common modifiers to their left and right version when copying to the keyboard manager state variable if remappings from common modifiers are passed
|
||||
TEST_METHOD (ApplySingleKeyRemappings_ShouldSplitRemappingsFromCommonModifiers_OnPassingBufferWithSomeMappingsFromCommonModifiers)
|
||||
{
|
||||
KeyboardManagerState testState;
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Add Ctrl->A, Alt->B, Shift->C and Win->D remappings to the buffer
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ VK_CONTROL, 0x41 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ VK_MENU, 0x42 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ VK_SHIFT, 0x43 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ CommonSharedConstants::VK_WIN_BOTH, 0x44 }), std::wstring()));
|
||||
|
||||
// Apply the single key remaps from the buffer to the keyboard manager state variable
|
||||
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(testState, remapBuffer, false);
|
||||
|
||||
// Expected LCtrl/RCtrl remapped to A, LAlt/RAlt to B, LShift/RShift to C, LWin/RWin to D
|
||||
SingleKeyRemapTable expectedTable;
|
||||
expectedTable[VK_LCONTROL] = 0x41;
|
||||
expectedTable[VK_RCONTROL] = 0x41;
|
||||
expectedTable[VK_LMENU] = 0x42;
|
||||
expectedTable[VK_RMENU] = 0x42;
|
||||
expectedTable[VK_LSHIFT] = 0x43;
|
||||
expectedTable[VK_RSHIFT] = 0x43;
|
||||
expectedTable[VK_LWIN] = 0x44;
|
||||
expectedTable[VK_RWIN] = 0x44;
|
||||
|
||||
bool areTablesEqual = (expectedTable == testState.singleKeyReMap);
|
||||
Assert::AreEqual(true, areTablesEqual);
|
||||
}
|
||||
|
||||
// Test if the ApplyShortcutRemappings method resets the keyboard manager state's os level and app specific shortcut remappings on passing an empty buffer
|
||||
TEST_METHOD (ApplyShortcutRemappings_ShouldResetShortcutRemappings_OnPassingEmptyBuffer)
|
||||
{
|
||||
KeyboardManagerState testState;
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Remap Ctrl+A to Ctrl+B for all apps and Ctrl+C to Alt+V for testApp1
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x41);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x42);
|
||||
Shortcut src2;
|
||||
src2.SetKey(VK_CONTROL);
|
||||
src2.SetKey(0x43);
|
||||
Shortcut dest2;
|
||||
dest2.SetKey(VK_MENU);
|
||||
dest2.SetKey(0x56);
|
||||
testState.AddOSLevelShortcut(src1, dest1);
|
||||
testState.AddAppSpecificShortcut(testApp1, src1, dest1);
|
||||
|
||||
// Apply the shortcut remaps from the buffer to the keyboard manager state variable
|
||||
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(testState, remapBuffer, false);
|
||||
|
||||
// Assert that shortcut remappings in the kbm state variable is empty
|
||||
Assert::AreEqual((size_t)0, testState.osLevelShortcutReMap.size());
|
||||
Assert::AreEqual((size_t)0, testState.appSpecificShortcutReMap.size());
|
||||
}
|
||||
|
||||
// Test if the ApplyShortcutRemappings method copies only the valid remappings to the keyboard manager state variable when some of the remappings are invalid
|
||||
TEST_METHOD (ApplyShortcutRemappings_ShouldCopyOnlyValidRemappings_OnPassingBufferWithSomeInvalidRemappings)
|
||||
{
|
||||
KeyboardManagerState testState;
|
||||
RemapBuffer remapBuffer;
|
||||
|
||||
// Add Ctrl+A->Ctrl+B, Ctrl+C->Alt+V, Ctrl+F->incomplete shortcut and Ctrl+G->incomplete key os level remappings to buffer
|
||||
// Add Ctrl+F->Alt+V, Ctrl+G->Ctrl+B, Ctrl+A->incomplete shortcut and Ctrl+C->incomplete key app specific remappings to buffer
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_CONTROL);
|
||||
src1.SetKey(0x41);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x42);
|
||||
Shortcut src2;
|
||||
src2.SetKey(VK_CONTROL);
|
||||
src2.SetKey(0x43);
|
||||
Shortcut dest2;
|
||||
dest2.SetKey(VK_MENU);
|
||||
dest2.SetKey(0x56);
|
||||
Shortcut src3;
|
||||
src3.SetKey(VK_CONTROL);
|
||||
src3.SetKey(0x46);
|
||||
Shortcut src4;
|
||||
src4.SetKey(VK_CONTROL);
|
||||
src4.SetKey(0x47);
|
||||
Shortcut dest4;
|
||||
dest4.SetKey(VK_CONTROL);
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src2, dest2 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src3, NULL }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src4, dest4 }), std::wstring()));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src3, dest2 }), testApp1));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src4, dest1 }), testApp1));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, NULL }), testApp1));
|
||||
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src2, dest4 }), testApp1));
|
||||
|
||||
// Apply the shortcut remaps from the buffer to the keyboard manager state variable
|
||||
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(testState, remapBuffer, false);
|
||||
|
||||
// Ctrl+A->Ctrl+B and Ctrl+C->Alt+V
|
||||
ShortcutRemapTable expectedOSLevelTable;
|
||||
expectedOSLevelTable[src1] = RemapShortcut(dest1);
|
||||
expectedOSLevelTable[src2] = RemapShortcut(dest2);
|
||||
|
||||
// Ctrl+F->Alt+V and Ctrl+G->Ctrl+B for testApp1
|
||||
AppSpecificShortcutRemapTable expectedAppSpecificLevelTable;
|
||||
expectedAppSpecificLevelTable[testApp1][src3] = RemapShortcut(dest2);
|
||||
expectedAppSpecificLevelTable[testApp1][src4] = RemapShortcut(dest1);
|
||||
|
||||
bool areOSLevelTablesEqual = (expectedOSLevelTable == testState.osLevelShortcutReMap);
|
||||
bool areAppSpecificTablesEqual = (expectedAppSpecificLevelTable == testState.appSpecificShortcutReMap);
|
||||
Assert::AreEqual(true, areOSLevelTablesEqual);
|
||||
Assert::AreEqual(true, areAppSpecificTablesEqual);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
#include "pch.h"
|
||||
#include "MockedInput.h"
|
||||
|
||||
// Set the keyboard hook procedure to be tested
|
||||
void MockedInput::SetHookProc(std::function<intptr_t(LowlevelKeyboardEvent*)> hookProcedure)
|
||||
{
|
||||
hookProc = hookProcedure;
|
||||
}
|
||||
|
||||
// Function to simulate keyboard input - arguments and return value based on SendInput function (https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput)
|
||||
UINT MockedInput::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize)
|
||||
{
|
||||
// Iterate over inputs
|
||||
for (UINT i = 0; i < cInputs; i++)
|
||||
{
|
||||
LowlevelKeyboardEvent keyEvent;
|
||||
|
||||
// Distinguish between key and sys key by checking if the key is either F10 (for syskeydown) or if the key message is sent while Alt is held down. SYSKEY messages are also sent if there is no window in focus, but that has not been mocked since it would require many changes. More details on key messages at https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-syskeydown
|
||||
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
|
||||
{
|
||||
if (keyboardState[VK_MENU] == true)
|
||||
{
|
||||
keyEvent.wParam = WM_SYSKEYUP;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyEvent.wParam = WM_KEYUP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pInputs[i].ki.wVk == VK_F10 || keyboardState[VK_MENU] == true)
|
||||
{
|
||||
keyEvent.wParam = WM_SYSKEYDOWN;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyEvent.wParam = WM_KEYDOWN;
|
||||
}
|
||||
}
|
||||
KBDLLHOOKSTRUCT lParam = {};
|
||||
|
||||
// Set only vkCode and dwExtraInfo since other values are unused
|
||||
lParam.vkCode = pInputs[i].ki.wVk;
|
||||
lParam.dwExtraInfo = pInputs[i].ki.dwExtraInfo;
|
||||
keyEvent.lParam = &lParam;
|
||||
|
||||
// If the SendVirtualInput call condition is true, increment the count. If no condition is set then always increment the count
|
||||
if (sendVirtualInputCallCondition == nullptr || sendVirtualInputCallCondition(&keyEvent))
|
||||
{
|
||||
sendVirtualInputCallCount++;
|
||||
}
|
||||
|
||||
// Call low level hook handler
|
||||
intptr_t result = MockedKeyboardHook(&keyEvent);
|
||||
|
||||
// Set keyboard state if the hook does not suppress the input
|
||||
if (result == 0)
|
||||
{
|
||||
// If key up flag is set, then set keyboard state to false
|
||||
keyboardState[pInputs[i].ki.wVk] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||
|
||||
// Handling modifier key codes
|
||||
switch (pInputs[i].ki.wVk)
|
||||
{
|
||||
case VK_CONTROL:
|
||||
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
|
||||
{
|
||||
keyboardState[VK_LCONTROL] = false;
|
||||
keyboardState[VK_RCONTROL] = false;
|
||||
}
|
||||
break;
|
||||
case VK_LCONTROL:
|
||||
keyboardState[VK_CONTROL] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||
break;
|
||||
case VK_RCONTROL:
|
||||
keyboardState[VK_CONTROL] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||
break;
|
||||
case VK_MENU:
|
||||
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
|
||||
{
|
||||
keyboardState[VK_LMENU] = false;
|
||||
keyboardState[VK_RMENU] = false;
|
||||
}
|
||||
break;
|
||||
case VK_LMENU:
|
||||
keyboardState[VK_MENU] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||
break;
|
||||
case VK_RMENU:
|
||||
keyboardState[VK_MENU] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||
break;
|
||||
case VK_SHIFT:
|
||||
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
|
||||
{
|
||||
keyboardState[VK_LSHIFT] = false;
|
||||
keyboardState[VK_RSHIFT] = false;
|
||||
}
|
||||
break;
|
||||
case VK_LSHIFT:
|
||||
keyboardState[VK_SHIFT] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||
break;
|
||||
case VK_RSHIFT:
|
||||
keyboardState[VK_SHIFT] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cInputs;
|
||||
}
|
||||
|
||||
// Function to simulate keyboard hook behavior
|
||||
intptr_t MockedInput::MockedKeyboardHook(LowlevelKeyboardEvent* data)
|
||||
{
|
||||
// If the hookProc is set to null, then skip the hook
|
||||
if (hookProc != nullptr)
|
||||
{
|
||||
return hookProc(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to get the state of a particular key
|
||||
bool MockedInput::GetVirtualKeyState(int key)
|
||||
{
|
||||
return keyboardState[key];
|
||||
}
|
||||
|
||||
// Function to reset the mocked keyboard state
|
||||
void MockedInput::ResetKeyboardState()
|
||||
{
|
||||
std::fill(keyboardState.begin(), keyboardState.end(), false);
|
||||
}
|
||||
|
||||
// Function to set SendVirtualInput call count condition
|
||||
void MockedInput::SetSendVirtualInputTestHandler(std::function<bool(LowlevelKeyboardEvent*)> condition)
|
||||
{
|
||||
sendVirtualInputCallCount = 0;
|
||||
sendVirtualInputCallCondition = condition;
|
||||
}
|
||||
|
||||
// Function to get SendVirtualInput call count
|
||||
int MockedInput::GetSendVirtualInputCallCount()
|
||||
{
|
||||
return sendVirtualInputCallCount;
|
||||
}
|
||||
|
||||
// Function to get the foreground process name
|
||||
void MockedInput::SetForegroundProcess(std::wstring process)
|
||||
{
|
||||
currentProcess = process;
|
||||
}
|
||||
|
||||
// Function to get the foreground process name
|
||||
void MockedInput::GetForegroundProcess(_Out_ std::wstring& foregroundProcess)
|
||||
{
|
||||
foregroundProcess = currentProcess;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
#include <keyboardmanager/common/InputInterface.h>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include <common/hooks/LowlevelKeyboardEvent.h>
|
||||
|
||||
// Class for mocked keyboard input
|
||||
class MockedInput :
|
||||
public KeyboardManagerInput::InputInterface
|
||||
{
|
||||
private:
|
||||
// Stores the states for all the keys - false for key up, and true for key down
|
||||
std::vector<bool> keyboardState;
|
||||
|
||||
// Function to be executed as a low level hook. By default it is nullptr so the hook is skipped
|
||||
std::function<intptr_t(LowlevelKeyboardEvent*)> hookProc;
|
||||
|
||||
// Stores the count of sendVirtualInput calls given if the condition sendVirtualInputCallCondition is satisfied
|
||||
int sendVirtualInputCallCount = 0;
|
||||
std::function<bool(LowlevelKeyboardEvent*)> sendVirtualInputCallCondition;
|
||||
|
||||
std::wstring currentProcess;
|
||||
|
||||
public:
|
||||
MockedInput()
|
||||
{
|
||||
keyboardState.resize(256, false);
|
||||
}
|
||||
|
||||
// Set the keyboard hook procedure to be tested
|
||||
void SetHookProc(std::function<intptr_t(LowlevelKeyboardEvent*)> hookProcedure);
|
||||
|
||||
// Function to simulate keyboard input
|
||||
UINT SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize);
|
||||
|
||||
// Function to simulate keyboard hook behavior
|
||||
intptr_t MockedKeyboardHook(LowlevelKeyboardEvent* data);
|
||||
|
||||
// Function to get the state of a particular key
|
||||
bool GetVirtualKeyState(int key);
|
||||
|
||||
// Function to reset the mocked keyboard state
|
||||
void ResetKeyboardState();
|
||||
|
||||
// Function to set SendVirtualInput call count condition
|
||||
void SetSendVirtualInputTestHandler(std::function<bool(LowlevelKeyboardEvent*)> condition);
|
||||
|
||||
// Function to get SendVirtualInput call count
|
||||
int GetSendVirtualInputCallCount();
|
||||
|
||||
// Function to get the foreground process name
|
||||
void SetForegroundProcess(std::wstring process);
|
||||
|
||||
// Function to get the foreground process name
|
||||
void GetForegroundProcess(_Out_ std::wstring& foregroundProcess);
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "pch.h"
|
||||
#include "TestHelpers.h"
|
||||
#include "MockedInput.h"
|
||||
#include "keyboardmanager/common/KeyboardManagerState.h"
|
||||
|
||||
namespace TestHelpers
|
||||
{
|
||||
// Function to reset the environment variables for tests
|
||||
void ResetTestEnv(MockedInput& input, KeyboardManagerState& state)
|
||||
{
|
||||
input.ResetKeyboardState();
|
||||
input.SetHookProc(nullptr);
|
||||
input.SetSendVirtualInputTestHandler(nullptr);
|
||||
input.SetForegroundProcess(L"");
|
||||
state.ClearSingleKeyRemaps();
|
||||
state.ClearOSLevelShortcuts();
|
||||
state.ClearAppSpecificShortcuts();
|
||||
|
||||
// Allocate memory for the keyboardManagerState activatedApp member to avoid CRT assert errors
|
||||
std::wstring maxLengthString;
|
||||
maxLengthString.resize(MAX_PATH);
|
||||
state.SetActivatedApp(maxLengthString);
|
||||
state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
class MockedInput;
|
||||
class KeyboardManagerState;
|
||||
|
||||
namespace TestHelpers
|
||||
{
|
||||
// Function to reset the environment variables for tests
|
||||
void ResetTestEnv(MockedInput& input, KeyboardManagerState& state);
|
||||
|
||||
// Function to return the index of the given key code from the drop down key list
|
||||
int GetDropDownIndexFromDropDownList(DWORD key, const std::vector<DWORD>& keyList);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <ProjectTelemetry.h>
|
||||
#include <shlwapi.h>
|
||||
#include <stdexcept>
|
||||
#include <unordered_set>
|
||||
#include "winrt/Windows.Foundation.h"
|
||||
@@ -0,0 +1,13 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by KeyboardManagerTest.rc
|
||||
|
||||
//////////////////////////////
|
||||
// Non-localizable
|
||||
|
||||
#define FILE_DESCRIPTION "PowerToys KeyboardManagerTest"
|
||||
#define INTERNAL_NAME "KeyboardManagerTest"
|
||||
#define ORIGINAL_FILENAME "KeyboardManagerTest.dll"
|
||||
|
||||
// Non-localizable
|
||||
//////////////////////////////
|
||||
Reference in New Issue
Block a user