mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-15 11:17:53 +01:00
[KBM] Fix shortcut remap scenarios that should/should not open start menu (#7171)
* Modify shortcut to key code more similar to shortcut to shortcuts code. Manually tested cases * Fixed existing tests and fixed scenario with other keys pressed and action key released * Fixed dummy key usage * Updated comments and removed dummy key usage in key to shortcut remaps * Added tests for disable and shortcut to key. Pending tests for dummy key * Added test cases for each usage of dummy key event * Remove redundant check
This commit is contained in:
@@ -9,14 +9,16 @@ public:
|
||||
KeyShortcutUnion targetShortcut;
|
||||
bool isShortcutInvoked;
|
||||
ModifierKey winKeyInvoked;
|
||||
// This bool value is only required for remapping shortcuts to Disable
|
||||
bool isOriginalActionKeyPressed;
|
||||
|
||||
RemapShortcut(const KeyShortcutUnion& sc) :
|
||||
targetShortcut(sc), isShortcutInvoked(false), winKeyInvoked(ModifierKey::Disabled)
|
||||
targetShortcut(sc), isShortcutInvoked(false), winKeyInvoked(ModifierKey::Disabled), isOriginalActionKeyPressed(false)
|
||||
{
|
||||
}
|
||||
|
||||
RemapShortcut() :
|
||||
targetShortcut(Shortcut()), isShortcutInvoked(false), winKeyInvoked(ModifierKey::Disabled)
|
||||
targetShortcut(Shortcut()), isShortcutInvoked(false), winKeyInvoked(ModifierKey::Disabled), isOriginalActionKeyPressed(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
else
|
||||
{
|
||||
key_count = std::get<Shortcut>(it->second).Size() + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
key_count = std::get<Shortcut>(it->second).Size();
|
||||
}
|
||||
LPINPUT keyEventList = new INPUT[size_t(key_count)]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
@@ -82,11 +82,11 @@ namespace KeyboardEventHandlers
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)targetShortcut.GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
i++;
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
// Dummy key is not required here since SetModifierKeyEvents will only add key-up events for the modifiers here, and the action key key-up is already sent before it
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
// Dummy key is not required here since SetModifierKeyEvents will only add key-down events for the modifiers here, and the action key key-down is already sent after it
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)targetShortcut.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
i++;
|
||||
@@ -248,7 +248,7 @@ namespace KeyboardEventHandlers
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Send dummy key
|
||||
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->Ctrl+V, press Win+A, since Win will be released here we need to send a dummy event before it
|
||||
int i = 0;
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
@@ -279,12 +279,14 @@ namespace KeyboardEventHandlers
|
||||
if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
key_count--;
|
||||
// Since the original shortcut's action key is pressed, set it to true
|
||||
it->second.isOriginalActionKeyPressed = true;
|
||||
}
|
||||
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Send dummy key
|
||||
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->V, press Win+A, since Win will be released here we need to send a dummy event before it
|
||||
int i = 0;
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
@@ -345,18 +347,18 @@ namespace KeyboardEventHandlers
|
||||
// if the released key is present in both shortcuts' modifiers (i.e part of the common modifiers)
|
||||
if (std::get<Shortcut>(it->second.targetShortcut).CheckWinKey(data->lParam->vkCode) || std::get<Shortcut>(it->second.targetShortcut).CheckCtrlKey(data->lParam->vkCode) || std::get<Shortcut>(it->second.targetShortcut).CheckAltKey(data->lParam->vkCode) || std::get<Shortcut>(it->second.targetShortcut).CheckShiftKey(data->lParam->vkCode))
|
||||
{
|
||||
// release all new shortcut keys and the common released modifier except the other common modifiers, and add all original shortcut modifiers except the common ones
|
||||
key_count = (dest_size - commonKeys) + (src_size - 1 - commonKeys);
|
||||
// release all new shortcut keys and the common released modifier except the other common modifiers, and add all original shortcut modifiers except the common ones, and dummy key
|
||||
key_count = (dest_size - commonKeys) + (src_size - 1 - commonKeys) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// release all new shortcut keys except the common modifiers and add all original shortcut modifiers except the common ones
|
||||
key_count = (dest_size - 1) + (src_size - 2) - (2 * (size_t)commonKeys);
|
||||
// release all new shortcut keys except the common modifiers and add all original shortcut modifiers except the common ones, and dummy key
|
||||
key_count = (dest_size - 1) + (src_size - 2) - (2 * (size_t)commonKeys) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
}
|
||||
|
||||
// If the target shortcut's action key is pressed, then it should be released
|
||||
bool isActionKeyPressed = false;
|
||||
if (GetAsyncKeyState(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()) & 0x8000)
|
||||
if (ii.GetVirtualKeyState((std::get<Shortcut>(it->second.targetShortcut).GetActionKey())))
|
||||
{
|
||||
isActionKeyPressed = true;
|
||||
key_count += 1;
|
||||
@@ -376,11 +378,14 @@ namespace KeyboardEventHandlers
|
||||
|
||||
// Set original shortcut key down state except the action key and the released modifier since the original action key may or may not be held down. If it is held down it will generate it's own key message
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut), data->lParam->vkCode);
|
||||
|
||||
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+Ctrl+A->Ctrl+V, press Win+Ctrl+A and release A then Ctrl, since Win will be pressed here we need to send a dummy event after it
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1 for releasing new key and original shortcut modifiers except the one released
|
||||
key_count = dest_size + src_size - 2;
|
||||
// 1 for releasing new key and original shortcut modifiers except the one released and dummy key
|
||||
key_count = dest_size + src_size - 2 + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
// Do not send Disable key up
|
||||
if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
@@ -400,10 +405,15 @@ namespace KeyboardEventHandlers
|
||||
|
||||
// Set original shortcut key down state except the action key and the released modifier since the original action key may or may not be held down. If it is held down it will generate it's own key message
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, Shortcut(), data->lParam->vkCode);
|
||||
|
||||
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+Ctrl+A->V, press Win+Ctrl+A and release A then Ctrl, since Win will be pressed here we need to send a dummy event after it
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
|
||||
// Reset the remap state
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp)
|
||||
{
|
||||
@@ -428,6 +438,8 @@ namespace KeyboardEventHandlers
|
||||
// In case of mapping to disable do not send anything
|
||||
if (!remapToShortcut && std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
// Since the original shortcut's action key is pressed, set it to true
|
||||
it->second.isOriginalActionKeyPressed = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -443,13 +455,12 @@ namespace KeyboardEventHandlers
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
|
||||
it->second.isShortcutInvoked = true;
|
||||
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Case 3: If the action key is released from the original shortcut keep modifiers of the new shortcut until some other key event which doesn't apply to the original shortcut
|
||||
// Case 3: If the action key is released from the original shortcut, keep modifiers of the new shortcut until some other key event which doesn't apply to the original shortcut
|
||||
if (data->lParam->vkCode == it->first.GetActionKey() && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP))
|
||||
{
|
||||
size_t key_count = 1;
|
||||
@@ -459,44 +470,54 @@ namespace KeyboardEventHandlers
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
it->second.isShortcutInvoked = true;
|
||||
}
|
||||
|
||||
// for remap from shortcut to key, when the action key is released, the remap invoke is completed so revert to original shortcut state
|
||||
// If remapped to disable, do nothing and suppress the key event
|
||||
else if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
// Since the original shortcut's action key is released, set it to false
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1 for releasing new key and original shortcut modifiers, and dummy key
|
||||
key_count = dest_size + (src_size - 1) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
// Do not send Disable key
|
||||
if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
// Check if the keyboard state is clear apart from the target remap key (by creating a temp Shortcut object with the target key)
|
||||
bool isKeyboardStateClear = Shortcut(std::vector<DWORD>({ KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)) })).IsKeyboardStateClearExceptShortcut(ii);
|
||||
// If the keyboard state is clear, we release the target key but do not reset the remap state
|
||||
if (isKeyboardStateClear)
|
||||
{
|
||||
key_count--;
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Release new key state
|
||||
int i = 0;
|
||||
// Do not send Disable key
|
||||
if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED)
|
||||
// If any other key is pressed, then the keyboard state must be reverted back to the physical keys. This is to take cases like Ctrl+A->D remap and user presses B+Ctrl+A and releases A, or Ctrl+A+B and releases A
|
||||
else
|
||||
{
|
||||
// 1 for releasing new key and original shortcut modifiers, and dummy key
|
||||
key_count = dest_size + (src_size - 1) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Release new key state
|
||||
int i = 0;
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
|
||||
// Set original shortcut key down state except the action key and the released modifier
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
// Set original shortcut key down state except the action key and the released modifier
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Send dummy key
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->V, press Shift+Win+A and release A, since Win will be pressed here we need to send a dummy event after it
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp)
|
||||
{
|
||||
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
|
||||
// Reset the remap state
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
||||
{
|
||||
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,13 +537,17 @@ namespace KeyboardEventHandlers
|
||||
if (std::get<Shortcut>(it->second.targetShortcut).GetCtrlKey() == NULL && std::get<Shortcut>(it->second.targetShortcut).GetAltKey() == NULL && std::get<Shortcut>(it->second.targetShortcut).GetShiftKey() == NULL)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
// If it is not remapped to Disable
|
||||
else if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
return 1;
|
||||
// Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)));
|
||||
}
|
||||
|
||||
// Suppress the modifier as it is already physically pressed
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Case 5: If any key apart from the action key or a modifier key in the original shortcut is pressed then revert the keyboard state to just the original modifiers being held down along with the current key press
|
||||
@@ -542,11 +567,11 @@ namespace KeyboardEventHandlers
|
||||
// If the original shortcut is a subset of the new shortcut
|
||||
if (commonKeys == src_size - 1)
|
||||
{
|
||||
key_count = dest_size - commonKeys + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
key_count = dest_size - commonKeys;
|
||||
|
||||
// If the target shortcut's action key is pressed, then it should be released and original shortcut's action key should be set
|
||||
bool isActionKeyPressed = false;
|
||||
if (GetAsyncKeyState(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()) & 0x8000)
|
||||
if (ii.GetVirtualKeyState((std::get<Shortcut>(it->second.targetShortcut).GetActionKey())))
|
||||
{
|
||||
isActionKeyPressed = true;
|
||||
key_count += 2;
|
||||
@@ -574,17 +599,16 @@ namespace KeyboardEventHandlers
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0);
|
||||
i++;
|
||||
|
||||
// Send dummy key since the current key pressed could be a modifier
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
// Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after shortcut to shortcut is released to open start menu
|
||||
}
|
||||
else
|
||||
{
|
||||
// Key up for all new shortcut keys, key down for original shortcut modifiers, dummy key and current key press but common keys aren't repeated
|
||||
key_count = (dest_size) + (src_size - 1) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE - (2 * (size_t)commonKeys);
|
||||
// Key up for all new shortcut keys, key down for original shortcut modifiers and current key press but common keys aren't repeated
|
||||
key_count = (dest_size) + (src_size - 1) - (2 * (size_t)commonKeys);
|
||||
|
||||
// If the target shortcut's action key is pressed, then it should be released and original shortcut's action key should be set
|
||||
bool isActionKeyPressed = false;
|
||||
if (GetAsyncKeyState(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()) & 0x8000)
|
||||
if (ii.GetVirtualKeyState((std::get<Shortcut>(it->second.targetShortcut).GetActionKey())))
|
||||
{
|
||||
isActionKeyPressed = true;
|
||||
key_count += 2;
|
||||
@@ -616,12 +640,13 @@ namespace KeyboardEventHandlers
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0);
|
||||
i++;
|
||||
|
||||
// Send dummy key
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
// Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after shortcut to shortcut is released to open start menu
|
||||
}
|
||||
|
||||
// Reset the remap state
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp)
|
||||
{
|
||||
@@ -632,44 +657,79 @@ namespace KeyboardEventHandlers
|
||||
delete[] keyEventList;
|
||||
return 1;
|
||||
}
|
||||
// All modifier keys and action key will be pressed down because if they are not pressed that means that handler has already been invoked on key release
|
||||
else if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
// For remap to key, if the original action key is not currently pressed, we should revert the keyboard state to the physical keys. If it is pressed we should not suppress the event so that shortcut to key remaps can be pressed with other keys. Example use-case: Alt+D->Win, allows Alt+D+A to perform Win+A
|
||||
else
|
||||
{
|
||||
// Key down for original shortcut modifiers and action key, dummy key, and current key press
|
||||
size_t key_count = src_size + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + 1;
|
||||
// Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps, Shift is pressed. System should not see Shift and Caps pressed together
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)));
|
||||
|
||||
LPINPUT keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
// If the shortcut is remapped to Disable then we have to revert the keyboard state to the physical keys
|
||||
bool isRemapToDisable = (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED);
|
||||
bool isOriginalActionKeyPressed = false;
|
||||
|
||||
// Set old shortcut key down state
|
||||
int i = 0;
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Set old action key
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
|
||||
// Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0);
|
||||
i++;
|
||||
|
||||
// Send dummy key
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
||||
if (!isRemapToDisable)
|
||||
{
|
||||
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
|
||||
// If the remap target key is currently pressed, then we do not have to revert the keyboard state to the physical keys
|
||||
if (ii.GetVirtualKeyState((KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)))))
|
||||
{
|
||||
isOriginalActionKeyPressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isOriginalActionKeyPressed = it->second.isOriginalActionKeyPressed;
|
||||
}
|
||||
|
||||
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
return 1;
|
||||
if (isRemapToDisable || !isOriginalActionKeyPressed)
|
||||
{
|
||||
// Key down for original shortcut modifiers and action key, and current key press
|
||||
size_t key_count = src_size + 1;
|
||||
|
||||
LPINPUT keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Set original shortcut key down state
|
||||
int i = 0;
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Send the original action key only if it is physically pressed. For remappings to keys other than disabled we already check earlier that it is not pressed in this scenario. For remap to disable
|
||||
if (isRemapToDisable && isOriginalActionKeyPressed)
|
||||
{
|
||||
// Set original action key
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
key_count--;
|
||||
}
|
||||
|
||||
// Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0);
|
||||
i++;
|
||||
|
||||
// Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after another shortcut to key remap is released to open start menu
|
||||
|
||||
// Reset the remap state
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
||||
{
|
||||
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
|
||||
}
|
||||
|
||||
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// For remap to key, nothing should be done since the shortcut should only get released on releasing any of the original shortcut keys.
|
||||
// Case 6: If any key apart from original modifier or original action key is released - This can't happen since the key down would have to happen first, which is handled above. If a key up message is generated for some other key (maybe by code) do not suppress it
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ namespace RemappingLogicTests
|
||||
});
|
||||
}
|
||||
|
||||
// Tests for shortcut to shortcut remappings
|
||||
|
||||
// Test if correct keyboard states are set for a 2 key shortcut remap wih different modifiers key down
|
||||
TEST_METHOD (RemappedTwoKeyShortcutWithDiffModifiers_ShouldSetTargetShortcutDown_OnKeyDown)
|
||||
{
|
||||
@@ -1115,6 +1117,8 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x58), true);
|
||||
}
|
||||
|
||||
// Tests for shortcut to key remappings
|
||||
|
||||
// Test if correct keyboard states are set for a 2 key shortcut to a single key remap not containing that key on key down followed by key up
|
||||
TEST_METHOD (RemappedTwoKeyShortcutToSingleKeyNotContainingThatKey_ShouldSetCorrectKeyStates_OnKeyEvents)
|
||||
{
|
||||
@@ -1287,8 +1291,8 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
}
|
||||
|
||||
// Test if keyboard state is reverted for a shortcut to a single key remap (target key is not a part of the shortcut) on key down followed by releasing the action key
|
||||
TEST_METHOD (RemappedShortcutToSingleKeyWhereKeyIsNotInShortcut_ShouldSetOriginalModifier_OnReleasingActionKey)
|
||||
// Test if keyboard state is not reverted for a shortcut to a single key remap (target key is not a part of the shortcut) on key down followed by releasing the action key
|
||||
TEST_METHOD (RemappedShortcutToSingleKeyWhereKeyIsNotInShortcut_ShouldNotSetOriginalModifier_OnReleasingActionKey)
|
||||
{
|
||||
// Remap Ctrl+A to Alt
|
||||
Shortcut src;
|
||||
@@ -1309,14 +1313,16 @@ namespace RemappingLogicTests
|
||||
// Press Ctrl+A, release A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A, Alt should be false, Ctrl should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
// Ctrl, A, Alt should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
// Shortcut invoked state should be true
|
||||
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
}
|
||||
|
||||
// Test if keyboard state is reverted for a shortcut to a single key remap (target key is a modifier in the shortcut) on key down followed by releasing the action key
|
||||
TEST_METHOD (RemappedShortcutToSingleKeyWhereKeyIsAModifierInShortcut_ShouldSetOriginalModifier_OnReleasingActionKey)
|
||||
// Test if keyboard state is not reverted for a shortcut to a single key remap (target key is a modifier in the shortcut) on key down followed by releasing the action key
|
||||
TEST_METHOD (RemappedShortcutToSingleKeyWhereKeyIsAModifierInShortcut_ShouldNotSetOriginalModifier_OnReleasingActionKey)
|
||||
{
|
||||
// Remap Ctrl+A to Ctrl
|
||||
Shortcut src;
|
||||
@@ -1337,13 +1343,15 @@ namespace RemappingLogicTests
|
||||
// Press Ctrl+A, release A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A should be false, Ctrl should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
// Both A and Ctrl should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
// Shortcut invoked state should be true
|
||||
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
}
|
||||
|
||||
// Test if keyboard state is reverted for a shortcut to a single key remap (target key is the action key in the shortcut) on key down followed by releasing the action key
|
||||
TEST_METHOD (RemappedShortcutToSingleKeyWhereKeyIsActionKeyInShortcut_ShouldSetOriginalModifier_OnReleasingActionKey)
|
||||
// Test if keyboard state is not reverted for a shortcut to a single key remap (target key is the action key in the shortcut) on key down followed by releasing the action key
|
||||
TEST_METHOD (RemappedShortcutToSingleKeyWhereKeyIsActionKeyInShortcut_ShouldNotSetOriginalModifier_OnReleasingActionKey)
|
||||
{
|
||||
// Remap Ctrl+A to A
|
||||
Shortcut src;
|
||||
@@ -1364,9 +1372,11 @@ namespace RemappingLogicTests
|
||||
// Press Ctrl+A, release A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A should be false, Ctrl should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
// Ctrl, A should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
// Shortcut invoked state should be true
|
||||
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
}
|
||||
|
||||
// Test if keyboard state is reverted for a shortcut to a single key remap (target key is not a part of the shortcut) on key down followed by releasing the modifier key
|
||||
@@ -1395,6 +1405,8 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
// Shortcut invoked state should be false
|
||||
Assert::AreEqual(false, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
}
|
||||
|
||||
// Test if keyboard state is reverted for a shortcut to a single key remap (target key is a modifier in the shortcut) on key down followed by releasing the modifier key
|
||||
@@ -1422,6 +1434,8 @@ namespace RemappingLogicTests
|
||||
// A, Ctrl should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
// Shortcut invoked state should be false
|
||||
Assert::AreEqual(false, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
}
|
||||
|
||||
// Test if keyboard state is reverted for a shortcut to a single key remap (target key is the action key in the shortcut) on key down followed by releasing the modifier key
|
||||
@@ -1479,6 +1493,39 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
}
|
||||
|
||||
// Test if remap is invoked for a shortcut to a single key remap and the keyboard state is reverted back to the physical keys when the shortcut is invoked along with other keys pressed before it and the action key is released
|
||||
TEST_METHOD (RemappedShortcutToSingleKey_ShouldRevertBackToPhysicalKeys_IfOtherKeysArePressedAlongWithItAndThenActionKeyIsReleased)
|
||||
{
|
||||
// Remap Ctrl+A to Alt
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_MENU);
|
||||
|
||||
const int nInputs = 4;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x42;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_CONTROL;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
input[3].type = INPUT_KEYBOARD;
|
||||
input[3].ki.wVk = 0x41;
|
||||
input[3].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Press B+Ctrl+A, release A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Alt, A should be false, Ctrl, B should be true
|
||||
Assert::AreEqual(true, mockedInputHandler.GetVirtualKeyState(VK_CONTROL));
|
||||
Assert::AreEqual(false, mockedInputHandler.GetVirtualKeyState(0x41));
|
||||
Assert::AreEqual(false, mockedInputHandler.GetVirtualKeyState(VK_MENU));
|
||||
Assert::AreEqual(true, mockedInputHandler.GetVirtualKeyState(0x42));
|
||||
// Shortcut invoked state should be false
|
||||
Assert::AreEqual(false, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
}
|
||||
|
||||
// Test that remap is not invoked for a shortcut to a single key remap when a larger remapped shortcut to shortcut containing those shortcut keys is invoked
|
||||
TEST_METHOD (RemappedShortcutToSingleKey_ShouldNotBeInvoked_IfALargerRemappedShortcutToShortcutContainingThoseShortcutKeysIsInvoked)
|
||||
{
|
||||
@@ -1546,8 +1593,8 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
}
|
||||
|
||||
// Test if remap is invoked for a shortcut to a single key remap when the shortcut is invoked along with other keys pressed after it and then action key is released
|
||||
TEST_METHOD (RemappedShortcutToSingleKey_ShouldBeInvoked_IfOtherKeysArePressedAfterItAndActionKeyIsReleased)
|
||||
// Test if remap is invoked and then reverted to physical keys for a shortcut to a single key remap when the shortcut is invoked along with other keys pressed after it and then action key is released
|
||||
TEST_METHOD (RemappedShortcutToSingleKey_ShouldBeInvokedAndThenRevertToPhysicalKeys_IfOtherKeysArePressedAfterItAndActionKeyIsReleased)
|
||||
{
|
||||
// Remap Ctrl+A to Alt
|
||||
Shortcut src;
|
||||
@@ -1572,6 +1619,8 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
// Shortcut invoked state should be true
|
||||
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x41;
|
||||
@@ -1585,10 +1634,12 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
// Shortcut invoked state should be false
|
||||
Assert::AreEqual(false, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
}
|
||||
|
||||
// Test if remap is invoked for a shortcut to a single key remap when the shortcut is invoked along with other keys pressed after it and modifier key is released
|
||||
TEST_METHOD (RemappedShortcutToSingleKey_ShouldBeInvoked_IfOtherKeysArePressedAfterItAndModifierKeyIsReleased)
|
||||
// Test if remap is invoked and then reverted to physical keys for a shortcut to a single key remap when the shortcut is invoked along with other keys pressed after it and modifier key is released
|
||||
TEST_METHOD (RemappedShortcutToSingleKey_ShouldBeInvokedAndThenRevertToPhysicalKeys_IfOtherKeysArePressedAfterItAndModifierKeyIsReleased)
|
||||
{
|
||||
// Remap Ctrl+A to Alt
|
||||
Shortcut src;
|
||||
@@ -1613,6 +1664,8 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
// Shortcut invoked state should be true
|
||||
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
@@ -1626,6 +1679,53 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
// Shortcut invoked state should be false
|
||||
Assert::AreEqual(false, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
}
|
||||
|
||||
// Test if remap is invoked and then reverted to physical keys for a shortcut to a single key remap when the shortcut is invoked and action key is released and then other keys pressed after it
|
||||
TEST_METHOD (RemappedShortcutToSingleKey_ShouldBeInvokedAndThenRevertToPhysicalKeys_IfActionKeyIsReleasedAndThenOtherKeysArePressedAfterIt)
|
||||
{
|
||||
// Remap Ctrl+A to Alt
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_MENU);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Press Ctrl+A, release A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A, Ctrl, Alt should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
// Shortcut invoked state should be true
|
||||
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x42;
|
||||
input[0].ki.dwFlags = 0;
|
||||
|
||||
// Press B
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
|
||||
// A, Alt should be false, Ctrl, B should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
// Shortcut invoked state should be false
|
||||
Assert::AreEqual(false, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
}
|
||||
|
||||
// Test if Windows left key state is set when a shortcut remap to Win both is invoked
|
||||
@@ -1668,6 +1768,8 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
}
|
||||
|
||||
// Tests for interaction between shortcut to shortcut and shortcut to key remappings
|
||||
|
||||
// Test if invoking two remapped shortcuts that share modifiers, where the first one remaps to a key and the second one remaps to a shortcut, in succession sets the correct keyboard states
|
||||
TEST_METHOD (TwoRemappedShortcutsThatShareModifiersWhereFirstOneRemapsToAKeyAndSecondOneRemapsToAShortcut_ShouldSetRemappedKeyStates_OnPressingSecondShortcutActionKeyAfterInvokingFirstShortcutRemap)
|
||||
{
|
||||
@@ -1830,6 +1932,8 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x43), true);
|
||||
}
|
||||
|
||||
// Tests for IME Caps Lock workaround on shortcut remappings
|
||||
|
||||
// Test if SendVirtualInput is sent exactly once with the suppress flag when Win+CapsLock is remapped to shortcut containing Ctrl
|
||||
TEST_METHOD (HandleShortcutRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenWinCapsLockIsMappedToShortcutContainingCtrl)
|
||||
{
|
||||
@@ -1927,7 +2031,7 @@ namespace RemappingLogicTests
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = VK_CONTROL;
|
||||
|
||||
// Send LWin+CapsLock keydown followed by Ctrl
|
||||
// Send Ctrl+A keydown followed by Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly once with the above condition
|
||||
@@ -1964,13 +2068,83 @@ namespace RemappingLogicTests
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = VK_SHIFT;
|
||||
|
||||
// Send LWin+CapsLock keydown followed by Ctrl
|
||||
// Send Ctrl+A keydown followed by Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, 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 shortcut containing Ctrl is remapped to CapsLock and Ctrl is pressed again while shortcut remap is invoked
|
||||
TEST_METHOD (HandleShortcutRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenShortcutContainingCtrlIsMappedToCapsLockAndCtrlIsPressedWhileInvoked)
|
||||
{
|
||||
// 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+A to CapsLock
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_CAPITAL);
|
||||
|
||||
const int nInputs = 3;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = VK_CONTROL;
|
||||
|
||||
// Send Ctrl+A keydown followed by Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, 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 shortcut containing Ctrl is remapped to CapsLock and Shift is pressed again while shortcut remap is invoked
|
||||
TEST_METHOD (HandleShortcutRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenShortcutContainingCtrlIsMappedToCapsLockAndShiftIsPressedWhileInvoked)
|
||||
{
|
||||
// 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+A to CapsLock
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_CAPITAL);
|
||||
|
||||
const int nInputs = 3;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = VK_SHIFT;
|
||||
|
||||
// Send Ctrl+A keydown followed by Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly once with the above condition
|
||||
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
}
|
||||
|
||||
// Tests for all types of shortcut remappings
|
||||
|
||||
// Test that the shortcut remap state is not reset when an unrelated key up message is sent - required to handle programs sending dummy key up messages
|
||||
TEST_METHOD (ShortcutRemap_ShouldNotGetReset_OnSendingKeyUpForAKeyNotPresentInTheShortcutAfterInvokingTheShortcut)
|
||||
{
|
||||
@@ -2005,6 +2179,9 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
}
|
||||
|
||||
// Tests for shortcut disable remappings
|
||||
|
||||
// Test that shortcut is disabled if the current shortcut pressed matches the exact shortcut which was remapped to Disable
|
||||
TEST_METHOD (ShortcutDisable_ShouldDisableShortcut_OnExactMatch)
|
||||
{
|
||||
Shortcut src;
|
||||
@@ -2030,6 +2207,7 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(actionKey), false);
|
||||
}
|
||||
|
||||
// Test that shortcut is not disabled if the shortcut which was remapped to Disable is a subset of the keys currently pressed
|
||||
TEST_METHOD (ShortcutDisable_ShouldNotDisableShortcut_OnSubsetMatch)
|
||||
{
|
||||
Shortcut src;
|
||||
@@ -2052,11 +2230,13 @@ namespace RemappingLogicTests
|
||||
// send Ctrl+Shift+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Check that Ctrl+A was not released and Disable key was not sent
|
||||
// Check that Ctrl+Shift+A was not released and Disable key was not sent
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(actionKey), true);
|
||||
}
|
||||
|
||||
// Test that shortcut is not disabled if the shortcut which was remapped to Disable is pressed followed by another key
|
||||
TEST_METHOD (ShortcutDisable_ShouldNotDisableShortcutSuperset_AfterShorcutWasDisabled)
|
||||
{
|
||||
Shortcut src;
|
||||
@@ -2086,6 +2266,426 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(actionKey), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
// Shortcut invoked state should be false
|
||||
Assert::AreEqual(false, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
}
|
||||
|
||||
// Test that shortcut is not disabled if the shortcut which was remapped to Disable is pressed and the action key is released, followed by pressing another key
|
||||
TEST_METHOD (ShortcutDisable_ShouldNotDisableShortcutSuperset_AfterActionKeyWasReleasedAndAnotherKeyWasPressedAfterIt)
|
||||
{
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
WORD actionKey = 0x41;
|
||||
src.SetKey(actionKey);
|
||||
WORD disableKey = CommonSharedConstants::VK_DISABLED;
|
||||
|
||||
testState.AddOSLevelShortcut(src, disableKey);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = actionKey;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = actionKey;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// send Ctrl+A, release A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Check that no keys are pressed
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(actionKey), false);
|
||||
// Shortcut invoked state should be true
|
||||
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x42;
|
||||
// send B
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
|
||||
// Check that Ctrl+B was pressed
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(actionKey), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
// Shortcut invoked state should be false
|
||||
Assert::AreEqual(false, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
}
|
||||
|
||||
// Test that the isOriginalActionKeyPressed flag is set to true on exact match of the shortcut
|
||||
TEST_METHOD (ShortcutDisable_ShouldSetIsOriginalActionKeyPressed_OnExactMatch)
|
||||
{
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
WORD actionKey = 0x41;
|
||||
src.SetKey(actionKey);
|
||||
WORD disableKey = CommonSharedConstants::VK_DISABLED;
|
||||
|
||||
testState.AddOSLevelShortcut(src, disableKey);
|
||||
|
||||
const int nInputs = 2;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = actionKey;
|
||||
|
||||
// send Ctrl+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// IsOriginalActionKeyPressed state should be true
|
||||
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isOriginalActionKeyPressed);
|
||||
}
|
||||
|
||||
// Test that the isOriginalActionKeyPressed flag is set to false on releasing the action key
|
||||
TEST_METHOD (ShortcutDisable_ShouldResetIsOriginalActionKeyPressed_OnReleasingActionKey)
|
||||
{
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
WORD actionKey = 0x41;
|
||||
src.SetKey(actionKey);
|
||||
WORD disableKey = CommonSharedConstants::VK_DISABLED;
|
||||
|
||||
testState.AddOSLevelShortcut(src, disableKey);
|
||||
|
||||
const int nInputs = 2;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = actionKey;
|
||||
|
||||
// send Ctrl+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// IsOriginalActionKeyPressed state should be true
|
||||
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isOriginalActionKeyPressed);
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = actionKey;
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// release A
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
|
||||
// IsOriginalActionKeyPressed state should be false
|
||||
Assert::AreEqual(false, testState.osLevelShortcutReMap[src].isOriginalActionKeyPressed);
|
||||
}
|
||||
|
||||
// Test that the isOriginalActionKeyPressed flag is set to true on pressing the action key again after releasing the action key
|
||||
TEST_METHOD (ShortcutDisable_ShouldSetIsOriginalActionKeyPressed_OnPressingActionKeyAfterReleasingActionKey)
|
||||
{
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
WORD actionKey = 0x41;
|
||||
src.SetKey(actionKey);
|
||||
WORD disableKey = CommonSharedConstants::VK_DISABLED;
|
||||
|
||||
testState.AddOSLevelShortcut(src, disableKey);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = actionKey;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = actionKey;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// send Ctrl+A, release A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// IsOriginalActionKeyPressed state should be false
|
||||
Assert::AreEqual(false, testState.osLevelShortcutReMap[src].isOriginalActionKeyPressed);
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = actionKey;
|
||||
input[0].ki.dwFlags = 0;
|
||||
|
||||
// press A
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
|
||||
// IsOriginalActionKeyPressed state should be true
|
||||
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isOriginalActionKeyPressed);
|
||||
}
|
||||
|
||||
// Test that the isOriginalActionKeyPressed flag is set to false on releasing the modifier key
|
||||
TEST_METHOD (ShortcutDisable_ShouldResetIsOriginalActionKeyPressed_OnReleasingModifierKey)
|
||||
{
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
WORD actionKey = 0x41;
|
||||
src.SetKey(actionKey);
|
||||
WORD disableKey = CommonSharedConstants::VK_DISABLED;
|
||||
|
||||
testState.AddOSLevelShortcut(src, disableKey);
|
||||
|
||||
const int nInputs = 2;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = actionKey;
|
||||
|
||||
// send Ctrl+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// IsOriginalActionKeyPressed state should be true
|
||||
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isOriginalActionKeyPressed);
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// release Ctrl
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
|
||||
// IsOriginalActionKeyPressed state should be false
|
||||
Assert::AreEqual(false, testState.osLevelShortcutReMap[src].isOriginalActionKeyPressed);
|
||||
}
|
||||
|
||||
// Test that the isOriginalActionKeyPressed flag is set to false on pressing another key
|
||||
TEST_METHOD (ShortcutDisable_ShouldResetIsOriginalActionKeyPressed_OnPressingAnotherKey)
|
||||
{
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
WORD actionKey = 0x41;
|
||||
src.SetKey(actionKey);
|
||||
WORD disableKey = CommonSharedConstants::VK_DISABLED;
|
||||
|
||||
testState.AddOSLevelShortcut(src, disableKey);
|
||||
|
||||
const int nInputs = 2;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = actionKey;
|
||||
|
||||
// send Ctrl+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// IsOriginalActionKeyPressed state should be true
|
||||
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isOriginalActionKeyPressed);
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x42;
|
||||
input[0].ki.dwFlags = 0;
|
||||
|
||||
// press B
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
|
||||
// IsOriginalActionKeyPressed state should be false
|
||||
Assert::AreEqual(false, testState.osLevelShortcutReMap[src].isOriginalActionKeyPressed);
|
||||
}
|
||||
|
||||
// Tests for dummy key events in shortcut remaps
|
||||
|
||||
// Test if one set of dummy key events is sent before releasing the modifier when shortcut is remapped to a shortcut not containing original shortcut modifiers on invoking the shortcut. Example: Win+A->Ctrl+V, press Win+A, since Win will be released here we need to send a dummy event before it
|
||||
TEST_METHOD (HandleShortcutRemapEvent_ShouldSendOneSetOfDummyKeyEventsBeforeReleasingTheModifier_WhenShortcutIsRemappedToAShortcutNotContainingOriginalShortcutModifiersOnInvoke)
|
||||
{
|
||||
// Set sendvirtualinput call count condition to return true if the key event was a dummy key and LWin is pressed
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([this](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->vkCode == KeyboardManagerConstants::DUMMY_KEY && mockedInputHandler.GetVirtualKeyState(VK_LWIN))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remap Win+A to Ctrl+V
|
||||
Shortcut src;
|
||||
src.SetKey(CommonSharedConstants::VK_WIN_BOTH);
|
||||
src.SetKey(0x41);
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_CONTROL);
|
||||
dest.SetKey(0x56);
|
||||
testState.AddOSLevelShortcut(src, dest);
|
||||
|
||||
const int nInputs = 2;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_LWIN;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
|
||||
// Send LWin+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly twice with the above condition (since two dummy key events are sent in one set)
|
||||
Assert::AreEqual(2, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
// LWin should be released
|
||||
Assert::AreEqual(false, mockedInputHandler.GetVirtualKeyState(VK_LWIN));
|
||||
}
|
||||
|
||||
// Test if one set of dummy key events is sent after setting the modifier when three key shortcut is remapped to a shortcut on releasing action key and a modifier. Example: Win+Ctrl+A->Ctrl+V, press Win+Ctrl+A and release A then Ctrl, since Win will be pressed here we need to send a dummy event after it
|
||||
TEST_METHOD (HandleShortcutRemapEvent_ShouldSendOneSetOfDummyKeyEventsAfterSettingTheModifier_When3KeyShortcutIsRemappedToShortcutOnReleasingActionKeyAndAModifier)
|
||||
{
|
||||
// Remap Win+Ctrl+A to Ctrl+V
|
||||
Shortcut src;
|
||||
src.SetKey(CommonSharedConstants::VK_WIN_BOTH);
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_CONTROL);
|
||||
dest.SetKey(0x56);
|
||||
testState.AddOSLevelShortcut(src, dest);
|
||||
|
||||
const int nInputs = 3;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_LWIN;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_CONTROL;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
|
||||
// Send LWin+Ctrl+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Set sendvirtualinput call count condition to return true if the key event was a dummy key and LWin is pressed
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([this](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->vkCode == KeyboardManagerConstants::DUMMY_KEY && mockedInputHandler.GetVirtualKeyState(VK_LWIN))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Release Ctrl
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly twice with the above condition (since two dummy key events are sent in one set)
|
||||
Assert::AreEqual(2, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
// LWin should be pressed
|
||||
Assert::AreEqual(true, mockedInputHandler.GetVirtualKeyState(VK_LWIN));
|
||||
}
|
||||
|
||||
// Test if one set of dummy key events is sent before releasing the modifier when shortcut is remapped to a single key on invoking the shortcut. Example: Win+A->V, press Win+A, since Win will be released here we need to send a dummy event before it
|
||||
TEST_METHOD (HandleShortcutRemapEvent_ShouldSendOneSetOfDummyKeyEventsBeforeReleasingTheModifier_WhenShortcutIsRemappedToASingleKeyOnInvoke)
|
||||
{
|
||||
// Set sendvirtualinput call count condition to return true if the key event was a dummy key and LWin is pressed
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([this](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->vkCode == KeyboardManagerConstants::DUMMY_KEY && mockedInputHandler.GetVirtualKeyState(VK_LWIN))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remap Win+A toV
|
||||
Shortcut src;
|
||||
src.SetKey(CommonSharedConstants::VK_WIN_BOTH);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, 0x56);
|
||||
|
||||
const int nInputs = 2;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_LWIN;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
|
||||
// Send LWin+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly twice with the above condition (since two dummy key events are sent in one set)
|
||||
Assert::AreEqual(2, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
// LWin should be released
|
||||
Assert::AreEqual(false, mockedInputHandler.GetVirtualKeyState(VK_LWIN));
|
||||
}
|
||||
|
||||
// Test if one set of dummy key events is sent after setting the modifier when shortcut is remapped to a single key on releasing action key and a modifier. Example: Win+A->V, press Shift+Win+A and release A, since Win will be pressed here we need to send a dummy event after it
|
||||
TEST_METHOD (HandleShortcutRemapEvent_ShouldSendOneSetOfDummyKeyEventsAfterSettingTheModifier_WhenShortcutIsRemappedToSingleKeyOnReleasingActionKeyAndAModifier)
|
||||
{
|
||||
// Remap Win+Ctrl+A to V
|
||||
Shortcut src;
|
||||
src.SetKey(CommonSharedConstants::VK_WIN_BOTH);
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, 0x56);
|
||||
|
||||
const int nInputs = 3;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_LWIN;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_CONTROL;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
|
||||
// Send LWin+Ctrl+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Set sendvirtualinput call count condition to return true if the key event was a dummy key and LWin is pressed
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([this](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->vkCode == KeyboardManagerConstants::DUMMY_KEY && mockedInputHandler.GetVirtualKeyState(VK_LWIN))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Release Ctrl
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly twice with the above condition (since two dummy key events are sent in one set)
|
||||
Assert::AreEqual(2, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
// LWin should be pressed
|
||||
Assert::AreEqual(true, mockedInputHandler.GetVirtualKeyState(VK_LWIN));
|
||||
}
|
||||
|
||||
// Test if one set of dummy key events is sent after setting the modifier when shortcut is remapped to a single key on invoking shortcut after pressing another key and then releasing the action key. Example: Win+A->V, press Shift+Win+A and release A, since Win will be pressed here we need to send a dummy event after it
|
||||
TEST_METHOD (HandleShortcutRemapEvent_ShouldSendOneSetOfDummyKeyEventsAfterSettingTheModifier_WhenShortcutIsRemappedToSingleKeyOnInvokingTheShortcutAfterPressingAnotherKeyAndThenReleasingTheActionKey)
|
||||
{
|
||||
// Remap Win+A to V
|
||||
Shortcut src;
|
||||
src.SetKey(CommonSharedConstants::VK_WIN_BOTH);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, 0x56);
|
||||
|
||||
const int nInputs = 3;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_SHIFT;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_LWIN;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
|
||||
// Send Shift+LWin+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Set sendvirtualinput call count condition to return true if the key event was a dummy key and LWin is pressed
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([this](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->vkCode == KeyboardManagerConstants::DUMMY_KEY && mockedInputHandler.GetVirtualKeyState(VK_LWIN))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x41;
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Release A
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly twice with the above condition (since two dummy key events are sent in one set)
|
||||
Assert::AreEqual(2, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
// LWin should be pressed
|
||||
Assert::AreEqual(true, mockedInputHandler.GetVirtualKeyState(VK_LWIN));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user