Add workaround for IME Caps Lock issue (#4123)

* Add workaround for Japanese IME Caps Lock issue

* Changed tweak to reset modifier key rather than Caps Lock

* added tests

* Fixed test comment

* Moved environment initialization in KBM tests to TEST_METHOD_INITIALIZE
This commit is contained in:
Arjun Balgovind 2020-06-15 16:48:00 -07:00 committed by GitHub
parent f8cc129f04
commit 49cbcf01c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 131 additions and 189 deletions

View File

@ -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;
}
}
}

View File

@ -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);
};

View File

@ -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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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<intptr_t(LowlevelKeyboardEvent*)> 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);

View File

@ -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<bool(LowlevelKeyboardEvent*)> condition)
{
sendVirtualInputCallCount = 0;
sendVirtualInputCallCondition = condition;
}
// Function to get SendVirtualInput call count
int MockedInput::GetSendVirtualInputCallCount()
{
return sendVirtualInputCallCount;
}

View File

@ -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<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;
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<bool(LowlevelKeyboardEvent*)> condition);
// Function to get SendVirtualInput call count
int GetSendVirtualInputCallCount();
};

View File

@ -8,6 +8,7 @@ namespace TestHelpers
{
input.ResetKeyboardState();
input.SetHookProc(nullptr);
input.SetSendVirtualInputTestHandler(nullptr);
state.ClearSingleKeyRemaps();
state.ClearOSLevelShortcuts();
}