diff --git a/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp b/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp index 16aeeb68cb..469819a2f3 100644 --- a/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp +++ b/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp @@ -32,6 +32,12 @@ namespace KeyboardEventHandlers target = VK_LWIN; } + // If Ctrl/Alt/Shift is being remapped to Caps Lock, then reset the modifier key state to fix issues in certain IME keyboards where the IME shortcut gets invoked since it detects that the modifier and Caps Lock is pressed even though it is suppressed by the hook - More information at the GitHub issue https://github.com/microsoft/PowerToys/issues/3397 + if (target == VK_CAPITAL && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)) + { + ResetIfModifierKeyForLowerLevelKeyHandlers(ii, it->first); + } + if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) { KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); @@ -44,6 +50,13 @@ namespace KeyboardEventHandlers lock.unlock(); UINT res = ii.SendVirtualInput(key_count, keyEventList, sizeof(INPUT)); delete[] keyEventList; + + // If Caps Lock is being remapped to Ctrl/Alt/Shift, then reset the modifier key state to fix issues in certain IME keyboards where the IME shortcut gets invoked since it detects that the modifier and Caps Lock is pressed even though it is suppressed by the hook - More information at the GitHub issue https://github.com/microsoft/PowerToys/issues/3397 + if (it->first == VK_CAPITAL && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)) + { + ResetIfModifierKeyForLowerLevelKeyHandlers(ii, target); + } + return 1; } } @@ -611,10 +624,27 @@ namespace KeyboardEventHandlers LPINPUT keyEventList = new INPUT[size_t(key_count)](); memset(keyEventList, 0, sizeof(keyEventList)); - // Use the shortcut flag to ensure these are not intercepted by any remapped keys or shortcuts + // Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, VK_NUMLOCK, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); KeyboardManagerHelper::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, VK_NUMLOCK, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); delete[] keyEventList; } + + // Function to ensure Ctrl/Shift/Alt modifier key state is not detected as pressed down by applications which detect keys at a lower level than hooks when it is remapped + void ResetIfModifierKeyForLowerLevelKeyHandlers(InputInterface& ii, DWORD key) + { + // If the argument is either of the Ctrl/Shift/Alt modifier key codes + if (KeyboardManagerHelper::IsModifierKey(key) && !(key == VK_LWIN || key == VK_RWIN || key == CommonSharedConstants::VK_WIN_BOTH)) + { + int key_count = 1; + LPINPUT keyEventList = new INPUT[size_t(key_count)](); + memset(keyEventList, 0, sizeof(keyEventList)); + + // Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts + KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)key, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); + UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + delete[] keyEventList; + } + } } diff --git a/src/modules/keyboardmanager/dll/KeyboardEventHandlers.h b/src/modules/keyboardmanager/dll/KeyboardEventHandlers.h index bf2d8a6191..2084c66d63 100644 --- a/src/modules/keyboardmanager/dll/KeyboardEventHandlers.h +++ b/src/modules/keyboardmanager/dll/KeyboardEventHandlers.h @@ -22,4 +22,7 @@ namespace KeyboardEventHandlers // Function to ensure Num Lock state does not change when it is suppressed by the low level hook void SetNumLockToPreviousState(InputInterface& ii); + + // Function to ensure Ctrl/Shift/Alt modifier key state is not detected as pressed down by applications which detect keys at a lower level than hooks when it is remapped + void ResetIfModifierKeyForLowerLevelKeyHandlers(InputInterface& ii, DWORD key); }; diff --git a/src/modules/keyboardmanager/test/KeyboardManagerRemapLogicTest.cpp b/src/modules/keyboardmanager/test/KeyboardManagerRemapLogicTest.cpp index db38e2201a..cff6c49a12 100644 --- a/src/modules/keyboardmanager/test/KeyboardManagerRemapLogicTest.cpp +++ b/src/modules/keyboardmanager/test/KeyboardManagerRemapLogicTest.cpp @@ -15,12 +15,15 @@ namespace KeyboardManagerRemapLogicTests TEST_CLASS (MockedInputSanityTests) { public: - // Test if mocked input is working - TEST_METHOD (MockedInput_ShouldSetKeyboardState_OnKeyEvent) + TEST_METHOD_INITIALIZE(InitializeTestEnv) { // Reset test environment TestHelpers::ResetTestEnv(mockedInputHandler, testState); + } + // Test if mocked input is working + TEST_METHOD (MockedInput_ShouldSetKeyboardState_OnKeyEvent) + { // Send key down and key up for A key (0x41) and check keyboard state both times const int nInputs = 1; INPUT input[nInputs] = {}; @@ -45,8 +48,7 @@ namespace KeyboardManagerRemapLogicTests TEST_CLASS (SingleKeyRemappingTests) { public: - // Test if correct keyboard states are set for a single key remap - TEST_METHOD (RemappedKey_ShouldSetTargetKeyState_OnKeyEvent) + TEST_METHOD_INITIALIZE(InitializeTestEnv) { // Reset test environment TestHelpers::ResetTestEnv(mockedInputHandler, testState); @@ -54,7 +56,11 @@ namespace KeyboardManagerRemapLogicTests // Set HandleSingleKeyRemapEvent as the hook procedure std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleSingleKeyRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); mockedInputHandler.SetHookProc(currentHookProc); + } + // Test if correct keyboard states are set for a single key remap + TEST_METHOD (RemappedKey_ShouldSetTargetKeyState_OnKeyEvent) + { // Remap A to B testState.AddSingleKeyRemap(0x41, 0x42); const int nInputs = 1; @@ -82,13 +88,6 @@ namespace KeyboardManagerRemapLogicTests // Test if key is suppressed if a key is disabled by single key remap TEST_METHOD (RemappedKeyDisabled_ShouldNotChangeKeyState_OnKeyEvent) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleSingleKeyRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleSingleKeyRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap A to 0x0 (disabled) testState.AddSingleKeyRemap(0x41, 0x0); const int nInputs = 1; @@ -114,13 +113,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a remap to Win (Both) key TEST_METHOD (RemappedKeyToWinBoth_ShouldSetWinLeftKeyState_OnKeyEvent) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleSingleKeyRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleSingleKeyRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap A to Common Win key testState.AddSingleKeyRemap(0x41, CommonSharedConstants::VK_WIN_BOTH); const int nInputs = 1; @@ -144,13 +136,64 @@ namespace KeyboardManagerRemapLogicTests Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LWIN), false); } + + // Test if SendVirtualInput is sent exactly once with the suppress flag when Caps Lock is remapped to Ctrl + TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirutalInputWithSuppressFlagExactlyOnce_WhenCapsLockIsMappedToCtrlAltShift) + { + // Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag + mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) { + if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG) + return true; + else + return false; + }); + + // Remap Caps Lock to Ctrl + testState.AddSingleKeyRemap(VK_CAPITAL, VK_CONTROL); + const int nInputs = 1; + + INPUT input[nInputs] = {}; + input[0].type = INPUT_KEYBOARD; + input[0].ki.wVk = VK_CAPITAL; + + // Send Caps Lock keydown + mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT)); + + // SendVirtualInput should be called exactly once with the above condition + Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount()); + } + + // Test if SendVirtualInput is sent exactly once with the suppress flag when Ctrl is remapped to Caps Lock + TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirutalInputWithSuppressFlagExactlyOnce_WhenCtrlAltShiftIsMappedToCapsLock) + { + // Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag + mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) { + if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG) + return true; + else + return false; + }); + + // Remap Ctrl to Caps Lock + testState.AddSingleKeyRemap(VK_CONTROL, VK_CAPITAL); + const int nInputs = 1; + + INPUT input[nInputs] = {}; + input[0].type = INPUT_KEYBOARD; + input[0].ki.wVk = VK_CONTROL; + + // Send Ctrl keydown + mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT)); + + // SendVirtualInput should be called exactly once with the above condition + Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount()); + } }; TEST_CLASS (OSLevelShortcutRemappingTests) { public: - // Test if correct keyboard states are set for a 2 key shortcut remap wih different modifiers key down - TEST_METHOD (RemappedTwoKeyShortcutWithDiffModifiers_ShouldSetTargetShortcutDown_OnKeyDown) + TEST_METHOD_INITIALIZE(InitializeTestEnv) { // Reset test environment TestHelpers::ResetTestEnv(mockedInputHandler, testState); @@ -158,7 +201,11 @@ namespace KeyboardManagerRemapLogicTests // Set HandleOSLevelShortcutRemapEvent as the hook procedure std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); mockedInputHandler.SetHookProc(currentHookProc); + } + // Test if correct keyboard states are set for a 2 key shortcut remap wih different modifiers key down + TEST_METHOD (RemappedTwoKeyShortcutWithDiffModifiers_ShouldSetTargetShortcutDown_OnKeyDown) + { // Remap Ctrl+A to Alt+V Shortcut src; src.SetKey(VK_CONTROL); @@ -188,13 +235,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 2 key shortcut remap with same modifiers key down TEST_METHOD (RemappedTwoKeyShortcutWithSameModifiers_ShouldSetTargetShortcutDown_OnKeyDown) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+A to Ctrl+V Shortcut src; src.SetKey(VK_CONTROL); @@ -223,13 +263,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 2 key shortcut remap with different modifiers key down followed by key up TEST_METHOD (RemappedTwoKeyShortcutWithDiffModifiers_ShouldClearKeyboard_OnKeyUp) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+A to Alt+V Shortcut src; src.SetKey(VK_CONTROL); @@ -265,13 +298,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 2 key shortcut remap with same modifiers key down followed by key up TEST_METHOD (RemappedTwoKeyShortcutWithSameModifiers_ShouldClearKeyboard_OnKeyUp) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+A to Ctrl+V Shortcut src; src.SetKey(VK_CONTROL); @@ -306,13 +332,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set when a 2 key shortcut is remapped, and a 3 key shortcut containing those keys is invoked - Ex: Ctrl+A remapped, but user presses Ctrl+Shift+A TEST_METHOD (RemappedTwoKeyShortcutInvokingAShortcutContainingThoseKeys_ShouldNotBeRemapped_OnKeyDown) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+A to Alt+V Shortcut src; src.SetKey(VK_CONTROL); @@ -345,13 +364,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 3 key shortcut remap wih different modifiers key down TEST_METHOD (RemappedThreeKeyShortcutWithDiffModifiers_ShouldSetTargetShortcutDown_OnKeyDown) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+Shift+A to Alt+LWin+V Shortcut src; src.SetKey(VK_CONTROL); @@ -387,13 +399,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 3 key shortcut remap wih partially different modifiers key down TEST_METHOD (RemappedThreeKeyShortcutWithPartiallyDiffModifiers_ShouldSetTargetShortcutDown_OnKeyDown) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+Shift+A to Alt+Ctrl+V Shortcut src; src.SetKey(VK_CONTROL); @@ -428,13 +433,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 3 key shortcut remap with same modifiers key down TEST_METHOD (RemappedThreeKeyShortcutWithSameModifiers_ShouldSetTargetShortcutDown_OnKeyDown) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+Shift+A to Ctrl+Shift+V Shortcut src; src.SetKey(VK_CONTROL); @@ -468,13 +466,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 3 key shortcut remap with different modifiers key down followed by key up TEST_METHOD (RemappedThreeKeyShortcutWithDiffModifiers_ShouldClearKeyboard_OnKeyUp) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+Shift+A to Alt+LWin+V Shortcut src; src.SetKey(VK_CONTROL); @@ -519,13 +510,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 3 key shortcut remap with partially different modifiers key down followed by key up TEST_METHOD (RemappedThreeKeyShortcutWithPartiallyDiffModifiers_ShouldClearKeyboard_OnKeyUp) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+Shift+A to Alt+Ctrl+V Shortcut src; src.SetKey(VK_CONTROL); @@ -569,13 +553,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 3 key shortcut remap with same modifiers key down followed by key up TEST_METHOD (RemappedThreeKeyShortcutWithSameModifiers_ShouldClearKeyboard_OnKeyUp) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+Shift+A to Ctrl+Shift+V Shortcut src; src.SetKey(VK_CONTROL); @@ -618,13 +595,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set when a 3 key shortcut is remapped, and a 2 key shortcut which is a subset of those keys is invoked - Ex: Ctrl+Shift+A remapped, but user presses Ctrl+A TEST_METHOD (RemappedThreeKeyShortcutInvokingAShortcutSubsetOfThoseKeys_ShouldNotBeRemapped_OnKeyDown) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+Shift+A to Alt+V Shortcut src; src.SetKey(VK_CONTROL); @@ -655,13 +625,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 3 key to 2 key shortcut remap with different modifiers key down TEST_METHOD (RemappedThreeKeyToTwoKeyShortcutWithDiffModifiers_ShouldSetTargetShortcutDown_OnKeyDown) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+Shift+A to Alt+V Shortcut src; src.SetKey(VK_CONTROL); @@ -695,13 +658,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 3 key to 2 key shortcut remap with partially different modifiers key down TEST_METHOD (RemappedThreeKeyToTwoKeyShortcutWithPartiallyDiffModifiers_ShouldSetTargetShortcutDown_OnKeyDown) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+Shift+A to Ctrl+V Shortcut src; src.SetKey(VK_CONTROL); @@ -734,13 +690,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 3 key to 2 key shortcut remap with different modifiers key down followed by key up TEST_METHOD (RemappedThreeKeyToTwoKeyShortcutWithDiffModifiers_ShouldClearKeyboard_OnKeyUp) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+Shift+A to Alt+V Shortcut src; src.SetKey(VK_CONTROL); @@ -783,13 +732,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 3 key to 2 key shortcut remap with partially different modifiers key down followed by key up TEST_METHOD (RemappedThreeKeyToTwoKeyShortcutWithPartiallyDiffModifiers_ShouldClearKeyboard_OnKeyUp) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+Shift+A to Ctrl+V Shortcut src; src.SetKey(VK_CONTROL); @@ -831,13 +773,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 2 key to 3 key shortcut remap with different modifiers key down TEST_METHOD (RemappedTwoKeyToThreeKeyShortcutWithDiffModifiers_ShouldSetTargetShortcutDown_OnKeyDown) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+A to Alt+Shift+V Shortcut src; src.SetKey(VK_CONTROL); @@ -869,13 +804,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 2 key to 3 key shortcut remap with partially different modifiers key down TEST_METHOD (RemappedTwoKeyToThreeKeyShortcutWithPartiallyDiffModifiers_ShouldSetTargetShortcutDown_OnKeyDown) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+A to Ctrl+Shift+V Shortcut src; src.SetKey(VK_CONTROL); @@ -906,13 +834,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 2 key to 3 key shortcut remap with different modifiers key down followed by key up TEST_METHOD (RemappedTwoKeyToThreeKeyShortcutWithDiffModifiers_ShouldClearKeyboard_OnKeyUp) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+A to Alt+Shift+V Shortcut src; src.SetKey(VK_CONTROL); @@ -950,13 +871,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a 2 key to 3 key shortcut remap with partially different modifiers key down followed by key up TEST_METHOD (RemappedTwoKeyToThreeKeyShortcutWithPartiallyDiffModifiers_ShouldClearKeyboard_OnKeyUp) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+A to Ctrl+Shift+V Shortcut src; src.SetKey(VK_CONTROL); @@ -993,13 +907,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set if a shortcut remap is pressed and then an unremapped shortcut with the same modifier is pressed - Ex: Ctrl+A is remapped. User invokes Ctrl+A then releases A and presses C (while Ctrl is held), should invoke Ctrl+C TEST_METHOD (InvokingUnremappedShortcutAfterRemappedShortcutWithSameModifier_ShouldSetUnremappedShortcut_OnKeyDown) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+A to Alt+Shift+V Shortcut src; src.SetKey(VK_CONTROL); @@ -1037,13 +944,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set for a shortcut remap with win both modifier TEST_METHOD (RemappedShortcutWithWinBothModifier_ShouldSetRemappedShortcut_OnKeyEvent) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Win+A to Alt+V Shortcut src; src.SetKey(CommonSharedConstants::VK_WIN_BOTH); @@ -1175,13 +1075,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set if a win both shortcut remap is pressed and then an unremapped shortcut with the LWin modifier is pressed TEST_METHOD (InvokingUnremappedShortcutWithLWinAfterRemappedShortcutWithWinBothModifier_ShouldSetUnremappedShortcutWithLWinKey_OnKeyDown) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Win+A to Alt+V Shortcut src; src.SetKey(CommonSharedConstants::VK_WIN_BOTH); @@ -1222,13 +1115,6 @@ namespace KeyboardManagerRemapLogicTests // Test if correct keyboard states are set if a win both shortcut remap is pressed and then an unremapped shortcut with the RWin modifier is pressed TEST_METHOD (InvokingUnremappedShortcutWithRWinAfterRemappedShortcutWithWinBothModifier_ShouldSetUnremappedShortcutWithRWinKey_OnKeyDown) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Win+A to Alt+V Shortcut src; src.SetKey(CommonSharedConstants::VK_WIN_BOTH); @@ -1269,13 +1155,6 @@ namespace KeyboardManagerRemapLogicTests // Test if target modifier is still held down even if the action key of the original shortcut is released - required for Alt+Tab/Win+Space cases TEST_METHOD (RemappedShortcutModifiers_ShouldBeDetectedAsPressed_OnReleasingActionKeyButHoldingModifiers) { - // Reset test environment - TestHelpers::ResetTestEnv(mockedInputHandler, testState); - - // Set HandleOSLevelShortcutRemapEvent as the hook procedure - std::function currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState)); - mockedInputHandler.SetHookProc(currentHookProc); - // Remap Ctrl+A to Alt+Tab Shortcut src; src.SetKey(VK_CONTROL); diff --git a/src/modules/keyboardmanager/test/MockedInput.cpp b/src/modules/keyboardmanager/test/MockedInput.cpp index 15ff38d2b8..c11908480c 100644 --- a/src/modules/keyboardmanager/test/MockedInput.cpp +++ b/src/modules/keyboardmanager/test/MockedInput.cpp @@ -45,6 +45,12 @@ UINT MockedInput::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize) 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); @@ -128,3 +134,16 @@ void MockedInput::ResetKeyboardState() { std::fill(keyboardState.begin(), keyboardState.end(), false); } + +// Function to set SendVirtualInput call count condition +void MockedInput::SetSendVirtualInputTestHandler(std::function condition) +{ + sendVirtualInputCallCount = 0; + sendVirtualInputCallCondition = condition; +} + +// Function to get SendVirtualInput call count +int MockedInput::GetSendVirtualInputCallCount() +{ + return sendVirtualInputCallCount; +} diff --git a/src/modules/keyboardmanager/test/MockedInput.h b/src/modules/keyboardmanager/test/MockedInput.h index 31ec836e36..4269c27806 100644 --- a/src/modules/keyboardmanager/test/MockedInput.h +++ b/src/modules/keyboardmanager/test/MockedInput.h @@ -15,6 +15,10 @@ private: // Function to be executed as a low level hook. By default it is nullptr so the hook is skipped std::function hookProc; + // Stores the count of sendVirtualInput calls given if the condition sendVirtualInputCallCondition is satisfied + int sendVirtualInputCallCount = 0; + std::function sendVirtualInputCallCondition; + public: MockedInput() { @@ -35,4 +39,10 @@ public: // Function to reset the mocked keyboard state void ResetKeyboardState(); + + // Function to set SendVirtualInput call count condition + void SetSendVirtualInputTestHandler(std::function condition); + + // Function to get SendVirtualInput call count + int GetSendVirtualInputCallCount(); }; diff --git a/src/modules/keyboardmanager/test/TestHelpers.cpp b/src/modules/keyboardmanager/test/TestHelpers.cpp index a7e5efef65..00b61cd372 100644 --- a/src/modules/keyboardmanager/test/TestHelpers.cpp +++ b/src/modules/keyboardmanager/test/TestHelpers.cpp @@ -8,6 +8,7 @@ namespace TestHelpers { input.ResetKeyboardState(); input.SetHookProc(nullptr); + input.SetSendVirtualInputTestHandler(nullptr); state.ClearSingleKeyRemaps(); state.ClearOSLevelShortcuts(); }