[KBM][CQ]Replace LPINPUT with std::vector<INPUT> (#32052)

This commit is contained in:
Masaru Iritani 2024-06-22 06:16:41 +09:00 committed by GitHub
parent 9509d7c1cc
commit 62c7b0a66d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 857 additions and 1361 deletions

View File

@ -72,7 +72,7 @@ In order to test the remapping logic, a mocked keyboard input handler had to be
The [`MockedInput`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/MockedInput.h) class uses a 256 size `bool` vector to store the key state for each key code. Identifying the foreground process is mocked by simply setting and getting a string value for the name of the current process. The [`MockedInput`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/MockedInput.h) class uses a 256 size `bool` vector to store the key state for each key code. Identifying the foreground process is mocked by simply setting and getting a string value for the name of the current process.
[To mock the `SendInput` method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/MockedInput.cpp#L10-L110), the steps for processing the input are as follows. This implementation is based on public documentation for SendInput and the behavior of key messages and keyboard hooks: [To mock the `SendInput` method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/MockedInput.cpp#L10-L110), the steps for processing the input are as follows. This implementation is based on public documentation for SendInput and the behavior of key messages and keyboard hooks:
- Iterate over all the inputs in the INPUT array argument - Iterate over all the inputs in the `INPUT` vector argument.
- If the event is a key up event, then it is considered [`WM_SYSKEYUP`](https://learn.microsoft.com/windows/win32/inputdev/wm-syskeyup) if Alt is held down, otherwise it is `WM_KEYUP`. - If the event is a key up event, then it is considered [`WM_SYSKEYUP`](https://learn.microsoft.com/windows/win32/inputdev/wm-syskeyup) if Alt is held down, otherwise it is `WM_KEYUP`.
- If the event is a key down event, then it is considered [`WM_SYSKEYDOWN`](https://learn.microsoft.com/windows/win32/inputdev/wm-syskeydown) if either Alt is held down or if it is F10, otherwise it is `WM_KEYDOWN`. - If the event is a key down event, then it is considered [`WM_SYSKEYDOWN`](https://learn.microsoft.com/windows/win32/inputdev/wm-syskeydown) if either Alt is held down or if it is F10, otherwise it is `WM_KEYDOWN`.
- An optional function which can be set on the `MockedInput` handler can be used to test for the number of times a key event is received by the system with a particular condition using [`sendVirtualInputCallCondition`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/MockedInput.cpp#L48-L52). - An optional function which can be set on the `MockedInput` handler can be used to test for the number of times a key event is received by the system with a particular condition using [`sendVirtualInputCallCondition`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/MockedInput.cpp#L48-L52).

View File

@ -69,7 +69,7 @@ namespace KeyboardEventHandlers
key_count = std::get<Shortcut>(it->second).Size(); key_count = std::get<Shortcut>(it->second).Size();
} }
LPINPUT keyEventList = new INPUT[size_t(key_count)]{}; std::vector<INPUT> keyEventList;
// Handle remaps to VK_WIN_BOTH // Handle remaps to VK_WIN_BOTH
DWORD target; DWORD target;
@ -92,35 +92,31 @@ namespace KeyboardEventHandlers
{ {
if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
{ {
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast<WORD>(target), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(target), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
} }
else else
{ {
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast<WORD>(target), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(target), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
} }
} }
else else
{ {
int i = 0;
Shortcut targetShortcut = std::get<Shortcut>(it->second); Shortcut targetShortcut = std::get<Shortcut>(it->second);
if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
{ {
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(targetShortcut.GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(targetShortcut.GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
i++; Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, false, 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 // 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 else
{ {
// 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 // 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
Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(targetShortcut.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(targetShortcut.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
i++;
} }
} }
UINT res = ii.SendVirtualInput(key_count, keyEventList, sizeof(INPUT)); ii.SendVirtualInput(keyEventList);
delete[] keyEventList;
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN) if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
{ {
@ -194,14 +190,12 @@ namespace KeyboardEventHandlers
return 1; return 1;
} }
} }
int key_count = 2; std::vector<INPUT> keyEventList;
LPINPUT keyEventList = new INPUT[size_t(key_count)](); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
Helpers::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
lock.unlock(); lock.unlock();
UINT res = ii.SendVirtualInput(key_count, keyEventList, sizeof(INPUT)); ii.SendVirtualInput(keyEventList);
delete[] keyEventList;
// Reset the long press flag when the key has been lifted. // Reset the long press flag when the key has been lifted.
if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
@ -306,8 +300,7 @@ namespace KeyboardEventHandlers
continue; continue;
} }
size_t key_count = 0; std::vector<INPUT> keyEventList;
LPINPUT keyEventList = nullptr;
// Remember which win key was pressed initially // Remember which win key was pressed initially
if (ii.GetVirtualKeyState(VK_RWIN)) if (ii.GetVirtualKeyState(VK_RWIN))
@ -331,8 +324,6 @@ namespace KeyboardEventHandlers
myThread.detach(); myThread.detach();
} }
Logger::trace(L"ChordKeyboardHandler:returning.."); Logger::trace(L"ChordKeyboardHandler:returning..");
return 1; return 1;
} }
@ -362,7 +353,6 @@ namespace KeyboardEventHandlers
} }
} }
auto threadFunction = [newUri]() { auto threadFunction = [newUri]() {
HINSTANCE result = ShellExecute(NULL, L"open", newUri.c_str(), NULL, NULL, SW_SHOWNORMAL); HINSTANCE result = ShellExecute(NULL, L"open", newUri.c_str(), NULL, NULL, SW_SHOWNORMAL);
@ -380,7 +370,6 @@ namespace KeyboardEventHandlers
myThread.detach(); myThread.detach();
} }
Logger::trace(L"ChordKeyboardHandler:returning.."); Logger::trace(L"ChordKeyboardHandler:returning..");
return 1; return 1;
} }
@ -394,30 +383,22 @@ namespace KeyboardEventHandlers
if (commonKeys == src_size - 1) if (commonKeys == src_size - 1)
{ {
// key down for all new shortcut keys except the common modifiers // key down for all new shortcut keys except the common modifiers
key_count = dest_size - commonKeys; keyEventList = std::vector<INPUT>{};
keyEventList = new INPUT[key_count]{}; Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
int i = 0; Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
} }
else else
{ {
// Dummy key, key up for all the original shortcut modifier keys and key down for all the new shortcut keys but common keys in each are not repeated // Dummy key, key up for all the original shortcut modifier keys and key down for all the new shortcut keys but common keys in each are not repeated
key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + (src_size - 1) + (dest_size) - (2 * static_cast<size_t>(commonKeys));
keyEventList = new INPUT[key_count]{};
// 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 // 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; Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// Release original shortcut state (release in reverse order of shortcut to be accurate) // Release original shortcut state (release in reverse order of shortcut to be accurate)
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut)); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut));
// Set new shortcut key down state // Set new shortcut key down state
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
} }
// Modifier state reset might be required for this key depending on the shortcut's action and target modifiers - ex: Win+Caps -> Ctrl+A // Modifier state reset might be required for this key depending on the shortcut's action and target modifiers - ex: Win+Caps -> Ctrl+A
@ -432,31 +413,23 @@ namespace KeyboardEventHandlers
} }
else if (remapToKey) else if (remapToKey)
{ {
// Dummy key, key up for all the original shortcut modifier keys and key down for remapped key
key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + (src_size - 1) + dest_size;
// Do not send Disable key // Do not send Disable key
if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED) 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 // Since the original shortcut's action key is pressed, set it to true
it->second.isOriginalActionKeyPressed = true; it->second.isOriginalActionKeyPressed = true;
} }
keyEventList = new INPUT[key_count]{};
// 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 // 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; Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// Release original shortcut state (release in reverse order of shortcut to be accurate) // Release original shortcut state (release in reverse order of shortcut to be accurate)
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// Set target key down state // Set target key down state
if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED) if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED)
{ {
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
} }
// Modifier state reset might be required for this key depending on the shortcut's action and target modifier - ex: Win+Caps -> Ctrl // Modifier state reset might be required for this key depending on the shortcut's action and target modifier - ex: Win+Caps -> Ctrl
@ -468,29 +441,14 @@ namespace KeyboardEventHandlers
// Remapped to text // Remapped to text
else else
{ {
key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + src_size; auto& remapping = std::get<std::wstring>(it->second.targetShortcut);
auto remapping = std::get<std::wstring>(it->second.targetShortcut); Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
const UINT inputEventCount = static_cast<UINT>(remapping.length() * 2);
key_count += inputEventCount;
keyEventList = new INPUT[key_count]{};
int i = 0;
Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// Release original shortcut state (release in reverse order of shortcut to be accurate) // Release original shortcut state (release in reverse order of shortcut to be accurate)
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
for (size_t idx = 0; idx < inputEventCount; ++idx) Helpers::SetTextKeyEvents(keyEventList, remapping);
{
auto& input = keyEventList[idx + i];
input.type = INPUT_KEYBOARD;
const bool upEvent = idx & 0x1;
input.ki.dwFlags = KEYEVENTF_UNICODE | (upEvent ? KEYEVENTF_KEYUP : 0);
input.ki.dwExtraInfo = KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG;
input.ki.wScan = remapping[idx >> 1];
}
} }
it->second.isShortcutInvoked = true; it->second.isShortcutInvoked = true;
@ -500,12 +458,9 @@ namespace KeyboardEventHandlers
state.SetActivatedApp(*activatedApp); state.SetActivatedApp(*activatedApp);
} }
Logger::trace(L"ChordKeyboardHandler:key_count:{}", key_count); Logger::trace(L"ChordKeyboardHandler:keyEventList.size:{}", keyEventList.size());
UINT res = ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT)); ii.SendVirtualInput(keyEventList);
delete[] keyEventList;
// Send daily telemetry event for Keyboard Manager key activation.
if (activatedApp.has_value()) if (activatedApp.has_value())
{ {
if (remapToKey) if (remapToKey)
@ -574,84 +529,41 @@ namespace KeyboardEventHandlers
if ((it->first.CheckWinKey(data->lParam->vkCode) || it->first.CheckCtrlKey(data->lParam->vkCode) || it->first.CheckAltKey(data->lParam->vkCode) || it->first.CheckShiftKey(data->lParam->vkCode)) && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)) if ((it->first.CheckWinKey(data->lParam->vkCode) || it->first.CheckCtrlKey(data->lParam->vkCode) || it->first.CheckAltKey(data->lParam->vkCode) || it->first.CheckShiftKey(data->lParam->vkCode)) && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP))
{ {
// Release new shortcut, and set original shortcut keys except the one released // Release new shortcut, and set original shortcut keys except the one released
size_t key_count = 0; std::vector<INPUT> keyEventList;
LPINPUT keyEventList = nullptr;
if (remapToShortcut && !isRunProgram) if (remapToShortcut && !isRunProgram)
{ {
// 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, 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, and dummy key
key_count = (dest_size - 1) + (src_size - 2) - (2 * static_cast<size_t>(commonKeys)) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
}
// If the target shortcut's action key is pressed, then it should be released // If the target shortcut's action key is pressed, then it should be released
bool isActionKeyPressed = false; bool isActionKeyPressed = ii.GetVirtualKeyState(std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
if (ii.GetVirtualKeyState((std::get<Shortcut>(it->second.targetShortcut).GetActionKey())))
{
isActionKeyPressed = true;
key_count += 1;
}
keyEventList = new INPUT[key_count]{};
// Release new shortcut state (release in reverse order of shortcut to be accurate) // Release new shortcut state (release in reverse order of shortcut to be accurate)
int i = 0;
if (isActionKeyPressed) if (isActionKeyPressed)
{ {
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
} }
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first, data->lParam->vkCode); Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first, data->lParam->vkCode);
// 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 // 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
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut), data->lParam->vkCode); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, 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 // 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
Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
} }
else if (remapToKey) else if (remapToKey)
{ {
// 1 for releasing new key and original shortcut modifiers except the one released and dummy key bool isTargetKeyPressed = (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED) && ii.GetVirtualKeyState(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)));
key_count = dest_size + src_size - 2 + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
bool isTargetKeyPressed = false;
// Do not send Disable key up
if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
{
key_count--;
}
else if (ii.GetVirtualKeyState(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))))
{
isTargetKeyPressed = true;
}
else
{
isTargetKeyPressed = false;
key_count--;
}
keyEventList = new INPUT[key_count]{};
// Release new key state // Release new key state
int i = 0;
if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED && isTargetKeyPressed) if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED && isTargetKeyPressed)
{ {
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(Helpers::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 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 // 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
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, Shortcut(), data->lParam->vkCode); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, 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 // 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
Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
} }
// Reset the remap state // Reset the remap state
@ -665,12 +577,7 @@ namespace KeyboardEventHandlers
state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
} }
// key count can be 0 if both shortcuts have same modifiers and the action key is not held down. delete will throw an error if keyEventList is empty ii.SendVirtualInput(keyEventList);
if (key_count > 0)
{
UINT res = ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT));
delete[] keyEventList;
}
return 1; return 1;
} }
@ -678,7 +585,7 @@ namespace KeyboardEventHandlers
if (!remapToShortcut || (remapToShortcut && std::get<Shortcut>(it->second.targetShortcut).CheckModifiersKeyboardState(ii))) if (!remapToShortcut || (remapToShortcut && std::get<Shortcut>(it->second.targetShortcut).CheckModifiersKeyboardState(ii)))
{ {
// Case 2: If the original shortcut is still held down the keyboard will get a key down message of the action key in the original shortcut and the new shortcut's modifiers will be held down (keys held down send repeated keydown messages) // Case 2: If the original shortcut is still held down the keyboard will get a key down message of the action key in the original shortcut and the new shortcut's modifiers will be held down (keys held down send repeated keydown messages)
if (((data->lParam->vkCode == it->first.GetActionKey() && !it->first.HasChord())||(data->lParam->vkCode == it->first.GetSecondKey() && it->first.HasChord())) && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)) if (((data->lParam->vkCode == it->first.GetActionKey() && !it->first.HasChord()) || (data->lParam->vkCode == it->first.GetSecondKey() && it->first.HasChord())) && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN))
{ {
// In case of mapping to disable do not send anything // In case of mapping to disable do not send anything
if (remapToKey && std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED) if (remapToKey && std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
@ -688,69 +595,46 @@ namespace KeyboardEventHandlers
return 1; return 1;
} }
size_t key_count = 1; std::vector<INPUT> keyEventList;
LPINPUT keyEventList = nullptr;
if (remapToShortcut) if (remapToShortcut)
{ {
keyEventList = new INPUT[key_count]{}; Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
} }
else if (remapToKey) else if (remapToKey)
{ {
keyEventList = new INPUT[key_count]{}; Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
} }
else if (remapToText) else if (remapToText)
{ {
auto remapping = std::get<std::wstring>(it->second.targetShortcut); auto& remapping = std::get<std::wstring>(it->second.targetShortcut);
const UINT inputEventCount = static_cast<UINT>(remapping.length() * 2); Helpers::SetTextKeyEvents(keyEventList, remapping);
key_count = inputEventCount;
keyEventList = new INPUT[key_count]{};
for (size_t idx = 0; idx < inputEventCount; ++idx)
{
auto& input = keyEventList[idx];
input.type = INPUT_KEYBOARD;
const bool upEvent = idx & 0x1;
input.ki.dwFlags = KEYEVENTF_UNICODE | (upEvent ? KEYEVENTF_KEYUP : 0);
input.ki.dwExtraInfo = KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG;
input.ki.wScan = remapping[idx >> 1];
}
} }
UINT res = ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT)); ii.SendVirtualInput(keyEventList);
delete[] keyEventList;
return 1; 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 (!remapToText && ((!it->first.HasChord() && data->lParam->vkCode == it->first.GetActionKey()) || (it->first.HasChord() && data->lParam->vkCode==it->first.GetSecondKey())) && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)) if (!remapToText && ((!it->first.HasChord() && data->lParam->vkCode == it->first.GetActionKey()) || (it->first.HasChord() && data->lParam->vkCode == it->first.GetSecondKey())) && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP))
{ {
size_t key_count = 1; std::vector<INPUT> keyEventList;
LPINPUT keyEventList = nullptr;
if (remapToShortcut && !it->first.HasChord()) if (remapToShortcut && !it->first.HasChord())
{ {
// Just lift the action key for no chords. // Just lift the action key for no chords.
keyEventList = new INPUT[key_count]{}; Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
} }
else if (remapToShortcut && it->first.HasChord()) else if (remapToShortcut && it->first.HasChord())
{ {
// If it has a chord, we'll want a full clean contemplated in the else, since you can't really repeat chords by pressing the end key again. // If it has a chord, we'll want a full clean contemplated in the else, since you can't really repeat chords by pressing the end key again.
// Key up for all new shortcut keys, key down for original shortcut modifiers and current key press but common keys aren't repeated // 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 * static_cast<size_t>(commonKeys)); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
int i = 0;
keyEventList = new INPUT[key_count]{};
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
// Release new shortcut state (release in reverse order of shortcut to be accurate) // Release new shortcut state (release in reverse order of shortcut to be accurate)
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
// Set old shortcut key down state // Set old shortcut key down state
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut)); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut));
// Reset the remap state // Reset the remap state
it->second.isShortcutInvoked = false; it->second.isShortcutInvoked = false;
@ -762,7 +646,6 @@ namespace KeyboardEventHandlers
{ {
state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
} }
} }
else if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED) else if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
{ {
@ -779,29 +662,21 @@ namespace KeyboardEventHandlers
// If the keyboard state is clear, we release the target key but do not reset the remap state // If the keyboard state is clear, we release the target key but do not reset the remap state
if (isKeyboardStateClear) if (isKeyboardStateClear)
{ {
keyEventList = new INPUT[key_count]{}; Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
} }
else else
{ {
// If any other key is pressed, then the keyboard state must be reverted back to the physical keys. // 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 // 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
// 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]{};
// Release new key state // Release new key state
int i = 0; Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
// Set original shortcut key down state except the action key // Set original shortcut key down state except the action key
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, 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 // 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
Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// Reset the remap state // Reset the remap state
it->second.isShortcutInvoked = false; it->second.isShortcutInvoked = false;
@ -816,8 +691,7 @@ namespace KeyboardEventHandlers
} }
} }
UINT res = ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT)); ii.SendVirtualInput(keyEventList);
delete[] keyEventList;
return 1; return 1;
} }
@ -854,8 +728,7 @@ namespace KeyboardEventHandlers
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, std::get<Shortcut>(it->second.targetShortcut).GetActionKey()); ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
} }
size_t key_count; std::vector<INPUT> keyEventList;
LPINPUT keyEventList = nullptr;
// Check if a new remapping should be applied // Check if a new remapping should be applied
Shortcut currentlyPressed = it->first; Shortcut currentlyPressed = it->first;
@ -868,40 +741,25 @@ namespace KeyboardEventHandlers
if (newRemapping.RemapToKey()) if (newRemapping.RemapToKey())
{ {
DWORD to = std::get<0>(newRemapping.targetShortcut); DWORD to = std::get<0>(newRemapping.targetShortcut);
bool isLastKeyStillPressed = ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey)); Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
key_count = static_cast<size_t>(from.Size()) - 1 + 1 + (isLastKeyStillPressed ? 1 : 0);
keyEventList = new INPUT[key_count]{};
int i = 0;
Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
if (ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey))) if (ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey)))
{ {
// If the action key from the last shortcut is still being pressed, release it. // If the action key from the last shortcut is still being pressed, release it.
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(from.actionKey), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(from.actionKey), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
} }
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(to), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(to), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
} }
else else
{ {
Shortcut to = std::get<Shortcut>(newRemapping.targetShortcut); Shortcut to = std::get<Shortcut>(newRemapping.targetShortcut);
bool isLastKeyStillPressed = ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey)); Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, to);
size_t temp_key_count_calculation = static_cast<size_t>(from.Size()) - 1;
temp_key_count_calculation += static_cast<size_t>(to.Size()) - 1;
temp_key_count_calculation -= static_cast<size_t>(2) * from.GetCommonModifiersCount(to);
key_count = temp_key_count_calculation + 1 + (isLastKeyStillPressed ? 1 : 0);
keyEventList = new INPUT[key_count]{};
int i = 0;
Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, to);
if (ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey))) if (ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey)))
{ {
// If the action key from the last shortcut is still being pressed, release it. // If the action key from the last shortcut is still being pressed, release it.
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(from.actionKey), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(from.actionKey), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
} }
Helpers::SetModifierKeyEvents(to, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, from); Helpers::SetModifierKeyEvents(to, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, from);
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(to.actionKey), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(to.actionKey), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
newRemapping.isShortcutInvoked = true; newRemapping.isShortcutInvoked = true;
} }
@ -917,41 +775,27 @@ namespace KeyboardEventHandlers
} }
else else
{ {
// 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 * static_cast<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 // 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; bool isActionKeyPressed = ii.GetVirtualKeyState(std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
if (ii.GetVirtualKeyState((std::get<Shortcut>(it->second.targetShortcut).GetActionKey())))
{
isActionKeyPressed = true;
key_count += 2;
}
keyEventList = new INPUT[key_count]{};
// Release new shortcut state (release in reverse order of shortcut to be accurate) // Release new shortcut state (release in reverse order of shortcut to be accurate)
int i = 0;
if (isActionKeyPressed) if (isActionKeyPressed)
{ {
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
} }
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
// Set old shortcut key down state // Set old shortcut key down state
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut)); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut));
// key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again // key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again
if (isActionKeyPressed) if (isActionKeyPressed)
{ {
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(it->first.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<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 // Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(data->lParam->vkCode), 0, 0); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<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 shortcut to shortcut is released to open start menu // 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
} }
@ -967,8 +811,7 @@ namespace KeyboardEventHandlers
state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
} }
UINT res = ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT)); ii.SendVirtualInput(keyEventList);
delete[] keyEventList;
return 1; return 1;
} }
else else
@ -1003,30 +846,20 @@ namespace KeyboardEventHandlers
if (isRemapToDisable || !isOriginalActionKeyPressed) if (isRemapToDisable || !isOriginalActionKeyPressed)
{ {
// Key down for original shortcut modifiers and action key, and current key press std::vector<INPUT> keyEventList;
size_t key_count = src_size + 1;
LPINPUT keyEventList = new INPUT[key_count]{};
// Set original shortcut key down state // Set original shortcut key down state
int i = 0; Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::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 // 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) if (isRemapToDisable && isOriginalActionKeyPressed)
{ {
// Set original action key // Set original action key
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(it->first.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<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 // Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(data->lParam->vkCode), 0, 0); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<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 // 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
@ -1041,8 +874,7 @@ namespace KeyboardEventHandlers
state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
} }
UINT res = ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT)); ii.SendVirtualInput(keyEventList);
delete[] keyEventList;
return 1; return 1;
} }
else else
@ -1344,7 +1176,7 @@ namespace KeyboardEventHandlers
if (targetPid != 0 && shortcut.alreadyRunningAction != Shortcut::ProgramAlreadyRunningAction::StartAnother) if (targetPid != 0 && shortcut.alreadyRunningAction != Shortcut::ProgramAlreadyRunningAction::StartAnother)
{ {
if (shortcut.alreadyRunningAction == Shortcut::ProgramAlreadyRunningAction::EndTask) if (shortcut.alreadyRunningAction == Shortcut::ProgramAlreadyRunningAction::EndTask)
{ {
TerminateProcessesByName(fileNamePart); TerminateProcessesByName(fileNamePart);
return; return;
@ -1803,13 +1635,11 @@ namespace KeyboardEventHandlers
// If the argument is either of the Ctrl/Shift/Alt modifier key codes // If the argument is either of the Ctrl/Shift/Alt modifier key codes
if (Helpers::IsModifierKey(key) && !(key == VK_LWIN || key == VK_RWIN || key == CommonSharedConstants::VK_WIN_BOTH)) if (Helpers::IsModifierKey(key) && !(key == VK_LWIN || key == VK_RWIN || key == CommonSharedConstants::VK_WIN_BOTH))
{ {
int key_count = 1; std::vector<INPUT> keyEventList;
LPINPUT keyEventList = new INPUT[size_t(key_count)]{};
// Use the suppress 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
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast<WORD>(key), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(key), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
UINT res = ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT)); ii.SendVirtualInput(keyEventList);
delete[] keyEventList;
} }
} }
} }
@ -1834,21 +1664,9 @@ namespace KeyboardEventHandlers
return 0; return 0;
} }
const size_t keyCount = remapping->length(); std::vector<INPUT> keyEventList;
const UINT eventCount = static_cast<UINT>(keyCount * 2); Helpers::SetTextKeyEvents(keyEventList, *remapping);
auto keyEventList = std::make_unique<INPUT[]>(keyCount * 2); ii.SendVirtualInput(keyEventList);
for (size_t i = 0; i < eventCount; ++i)
{
auto& input = keyEventList[i];
input.type = INPUT_KEYBOARD;
const bool upEvent = i & 0x1;
input.ki.dwFlags = KEYEVENTF_UNICODE | (upEvent ? KEYEVENTF_KEYUP : 0);
input.ki.dwExtraInfo = KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG;
input.ki.wScan = (*remapping)[i >> 1];
}
UINT res = ii.SendVirtualInput(eventCount, keyEventList.get(), sizeof(INPUT));
return 1; return 1;
} }

View File

@ -51,15 +51,13 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process // Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1); mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2; std::vector<INPUT> inputs{
INPUT input[nInputs] = {}; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
input[0].ki.wVk = VK_CONTROL; };
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
// Send Ctrl+A keydown // Send Ctrl+A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs);
// Ctrl and A key states should be unchanged, Alt and V key states should be true // Ctrl and A key states should be unchanged, Alt and V key states should be true
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
@ -83,15 +81,13 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process // Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp2); mockedInputHandler.SetForegroundProcess(testApp2);
const int nInputs = 2; std::vector<INPUT> inputs{
INPUT input[nInputs] = {}; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
input[0].ki.wVk = VK_CONTROL; };
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
// Send Ctrl+A keydown // Send Ctrl+A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs);
// Ctrl and A key states should be true, Alt and V key states should be false // Ctrl and A key states should be true, Alt and V key states should be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
@ -115,28 +111,24 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process // Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1); mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2; std::vector<INPUT> inputs1{
INPUT input[nInputs] = {}; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
input[0].ki.wVk = VK_CONTROL; };
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
// Send Ctrl+A keydown // Send Ctrl+A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs1);
// Activated app should be testApp1 // Activated app should be testApp1
Assert::AreEqual(testApp1, testState.GetActivatedApp()); Assert::AreEqual(testApp1, testState.GetActivatedApp());
input[0].type = INPUT_KEYBOARD; std::vector<INPUT> inputs2{
input[0].ki.wVk = 0x41; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } },
input[0].ki.dwFlags = KEYEVENTF_KEYUP; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL, .dwFlags = KEYEVENTF_KEYUP } }
input[1].type = INPUT_KEYBOARD; };
input[1].ki.wVk = VK_CONTROL;
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
// Release A then Ctrl // Release A then Ctrl
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs2);
// Activated app should be empty string // Activated app should be empty string
Assert::AreEqual(std::wstring(KeyboardManagerConstants::NoActivatedApp), testState.GetActivatedApp()); Assert::AreEqual(std::wstring(KeyboardManagerConstants::NoActivatedApp), testState.GetActivatedApp());
@ -156,28 +148,24 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process // Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1); mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2; std::vector<INPUT> inputs1{
INPUT input[nInputs] = {}; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
input[0].ki.wVk = VK_CONTROL; };
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
// Send Ctrl+A keydown // Send Ctrl+A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs1);
// Set the testApp as the foreground process // Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp2); mockedInputHandler.SetForegroundProcess(testApp2);
input[0].type = INPUT_KEYBOARD; std::vector<INPUT> inputs2{
input[0].ki.wVk = 0x41; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } },
input[0].ki.dwFlags = KEYEVENTF_KEYUP; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL, .dwFlags = KEYEVENTF_KEYUP } }
input[1].type = INPUT_KEYBOARD; };
input[1].ki.wVk = VK_CONTROL;
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
// Release A then Ctrl // Release A then Ctrl
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs2);
// Ctrl, A, Alt and Tab should all be false // Ctrl, A, Alt and Tab should all be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
@ -198,15 +186,13 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process // Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1); mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2; std::vector<INPUT> inputs{
INPUT input[nInputs] = {}; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
input[0].ki.wVk = VK_CONTROL; };
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
// Send Ctrl+A keydown // Send Ctrl+A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs);
// Ctrl and A key states should be unchanged, V key states should be true // Ctrl and A key states should be unchanged, V key states should be true
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
@ -226,15 +212,13 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process // Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp2); mockedInputHandler.SetForegroundProcess(testApp2);
const int nInputs = 2; std::vector<INPUT> inputs{
INPUT input[nInputs] = {}; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
input[0].ki.wVk = VK_CONTROL; };
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
// Send Ctrl+A keydown // Send Ctrl+A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs);
// Ctrl and A key states should be true, V key state should be false // Ctrl and A key states should be true, V key state should be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
@ -254,28 +238,24 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process // Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1); mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2; std::vector<INPUT> inputs1{
INPUT input[nInputs] = {}; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
input[0].ki.wVk = VK_CONTROL; };
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
// Send Ctrl+A keydown // Send Ctrl+A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs1);
// Activated app should be testApp1 // Activated app should be testApp1
Assert::AreEqual(testApp1, testState.GetActivatedApp()); Assert::AreEqual(testApp1, testState.GetActivatedApp());
input[0].type = INPUT_KEYBOARD; std::vector<INPUT> inputs2{
input[0].ki.wVk = 0x41; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } },
input[0].ki.dwFlags = KEYEVENTF_KEYUP; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL, .dwFlags = KEYEVENTF_KEYUP } }
input[1].type = INPUT_KEYBOARD; };
input[1].ki.wVk = VK_CONTROL;
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
// Release A then Ctrl // Release A then Ctrl
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs2);
// Activated app should be empty string // Activated app should be empty string
Assert::AreEqual(std::wstring(KeyboardManagerConstants::NoActivatedApp), testState.GetActivatedApp()); Assert::AreEqual(std::wstring(KeyboardManagerConstants::NoActivatedApp), testState.GetActivatedApp());
@ -292,28 +272,24 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process // Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1); mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2; std::vector<INPUT> inputs1{
INPUT input[nInputs] = {}; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
input[0].ki.wVk = VK_CONTROL; };
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
// Send Ctrl+A keydown // Send Ctrl+A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs1);
// Set the testApp as the foreground process // Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp2); mockedInputHandler.SetForegroundProcess(testApp2);
input[0].type = INPUT_KEYBOARD; std::vector<INPUT> inputs2{
input[0].ki.wVk = 0x41; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } },
input[0].ki.dwFlags = KEYEVENTF_KEYUP; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL, .dwFlags = KEYEVENTF_KEYUP } }
input[1].type = INPUT_KEYBOARD; };
input[1].ki.wVk = VK_CONTROL;
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
// Release A then Ctrl // Release A then Ctrl
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs2);
// Ctrl, A, V should all be false // Ctrl, A, V should all be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
@ -334,15 +310,13 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process // Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1); mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2; std::vector<INPUT> inputs{
INPUT input[nInputs] = {}; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = actionKey } }
input[0].ki.wVk = VK_CONTROL; };
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = actionKey;
// Send Ctrl+A keydown // Send Ctrl+A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs);
// Check if Ctrl+A is released and disable key was not send // Check if Ctrl+A is released and disable key was not send
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);

View File

@ -10,15 +10,15 @@ void MockedInput::SetHookProc(std::function<intptr_t(LowlevelKeyboardEvent*)> ho
} }
// Function to simulate keyboard input - arguments and return value based on SendInput function (https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-sendinput) // Function to simulate keyboard input - arguments and return value based on SendInput function (https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-sendinput)
UINT MockedInput::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int /*cbSize*/) void MockedInput::SendVirtualInput(const std::vector<INPUT>& inputs)
{ {
// Iterate over inputs // Iterate over inputs
for (UINT i = 0; i < cInputs; i++) for (const INPUT& input : inputs)
{ {
LowlevelKeyboardEvent keyEvent; LowlevelKeyboardEvent keyEvent{};
// Distinguish between key and sys key by checking if the key is either F10 (for syskeydown) or if the key message is sent while Alt is held down. SYSKEY messages are also sent if there is no window in focus, but that has not been mocked since it would require many changes. More details on key messages at https://learn.microsoft.com/windows/win32/inputdev/wm-syskeydown // Distinguish between key and sys key by checking if the key is either F10 (for syskeydown) or if the key message is sent while Alt is held down. SYSKEY messages are also sent if there is no window in focus, but that has not been mocked since it would require many changes. More details on key messages at https://learn.microsoft.com/windows/win32/inputdev/wm-syskeydown
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) if (input.ki.dwFlags & KEYEVENTF_KEYUP)
{ {
if (keyboardState[VK_MENU] == true) if (keyboardState[VK_MENU] == true)
{ {
@ -31,7 +31,7 @@ UINT MockedInput::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int /*cbSize*/
} }
else else
{ {
if (pInputs[i].ki.wVk == VK_F10 || keyboardState[VK_MENU] == true) if (input.ki.wVk == VK_F10 || keyboardState[VK_MENU] == true)
{ {
keyEvent.wParam = WM_SYSKEYDOWN; keyEvent.wParam = WM_SYSKEYDOWN;
} }
@ -43,8 +43,8 @@ UINT MockedInput::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int /*cbSize*/
KBDLLHOOKSTRUCT lParam = {}; KBDLLHOOKSTRUCT lParam = {};
// Set only vkCode and dwExtraInfo since other values are unused // Set only vkCode and dwExtraInfo since other values are unused
lParam.vkCode = pInputs[i].ki.wVk; lParam.vkCode = input.ki.wVk;
lParam.dwExtraInfo = pInputs[i].ki.dwExtraInfo; lParam.dwExtraInfo = input.ki.dwExtraInfo;
keyEvent.lParam = &lParam; keyEvent.lParam = &lParam;
// If the SendVirtualInput call condition is true, increment the count. If no condition is set then always increment the count // If the SendVirtualInput call condition is true, increment the count. If no condition is set then always increment the count
@ -60,55 +60,53 @@ UINT MockedInput::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int /*cbSize*/
if (result == 0) if (result == 0)
{ {
// If key up flag is set, then set keyboard state to false // If key up flag is set, then set keyboard state to false
keyboardState[pInputs[i].ki.wVk] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true; keyboardState[input.ki.wVk] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
// Handling modifier key codes // Handling modifier key codes
switch (pInputs[i].ki.wVk) switch (input.ki.wVk)
{ {
case VK_CONTROL: case VK_CONTROL:
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) if (input.ki.dwFlags & KEYEVENTF_KEYUP)
{ {
keyboardState[VK_LCONTROL] = false; keyboardState[VK_LCONTROL] = false;
keyboardState[VK_RCONTROL] = false; keyboardState[VK_RCONTROL] = false;
} }
break; break;
case VK_LCONTROL: case VK_LCONTROL:
keyboardState[VK_CONTROL] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true; keyboardState[VK_CONTROL] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break; break;
case VK_RCONTROL: case VK_RCONTROL:
keyboardState[VK_CONTROL] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true; keyboardState[VK_CONTROL] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break; break;
case VK_MENU: case VK_MENU:
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) if (input.ki.dwFlags & KEYEVENTF_KEYUP)
{ {
keyboardState[VK_LMENU] = false; keyboardState[VK_LMENU] = false;
keyboardState[VK_RMENU] = false; keyboardState[VK_RMENU] = false;
} }
break; break;
case VK_LMENU: case VK_LMENU:
keyboardState[VK_MENU] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true; keyboardState[VK_MENU] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break; break;
case VK_RMENU: case VK_RMENU:
keyboardState[VK_MENU] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true; keyboardState[VK_MENU] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break; break;
case VK_SHIFT: case VK_SHIFT:
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) if (input.ki.dwFlags & KEYEVENTF_KEYUP)
{ {
keyboardState[VK_LSHIFT] = false; keyboardState[VK_LSHIFT] = false;
keyboardState[VK_RSHIFT] = false; keyboardState[VK_RSHIFT] = false;
} }
break; break;
case VK_LSHIFT: case VK_LSHIFT:
keyboardState[VK_SHIFT] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true; keyboardState[VK_SHIFT] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break; break;
case VK_RSHIFT: case VK_RSHIFT:
keyboardState[VK_SHIFT] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true; keyboardState[VK_SHIFT] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break; break;
} }
} }
} }
return cInputs;
} }
// Function to simulate keyboard hook behavior // Function to simulate keyboard hook behavior

View File

@ -34,7 +34,7 @@ namespace KeyboardManagerInput
void SetHookProc(std::function<intptr_t(LowlevelKeyboardEvent*)> hookProcedure); void SetHookProc(std::function<intptr_t(LowlevelKeyboardEvent*)> hookProcedure);
// Function to simulate keyboard input // Function to simulate keyboard input
UINT SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize); void SendVirtualInput(const std::vector<INPUT>& inputs);
// Function to simulate keyboard hook behavior // Function to simulate keyboard hook behavior
intptr_t MockedKeyboardHook(LowlevelKeyboardEvent* data); intptr_t MockedKeyboardHook(LowlevelKeyboardEvent* data);

View File

@ -33,20 +33,22 @@ namespace RemappingLogicTests
TEST_METHOD (MockedInput_ShouldSetKeyboardState_OnKeyEvent) TEST_METHOD (MockedInput_ShouldSetKeyboardState_OnKeyEvent)
{ {
// Send key down and key up for A key (0x41) and check keyboard state both times // Send key down and key up for A key (0x41) and check keyboard state both times
const int nInputs = 1; std::vector<INPUT> inputs1{
INPUT input[nInputs] = {}; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } },
input[0].type = INPUT_KEYBOARD; };
input[0].ki.wVk = 0x41;
// Send A keydown // Send A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs1);
// A key state should be true // A key state should be true
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), true); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), true);
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
std::vector<INPUT> inputs2{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } },
};
// Send A keyup // Send A keyup
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs2);
// A key state should be false // A key state should be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);

View File

@ -34,7 +34,7 @@ namespace RemappingLogicTests
TEST_METHOD (SetKeyEvent_ShouldUseExtendedKeyFlag_WhenArgumentIsExtendedKey) TEST_METHOD (SetKeyEvent_ShouldUseExtendedKeyFlag_WhenArgumentIsExtendedKey)
{ {
const int nInputs = 15; const int nInputs = 15;
INPUT input[nInputs] = {}; std::vector<INPUT> inputs;
// List of extended keys // List of extended keys
WORD keyCodes[nInputs] = { VK_RCONTROL, VK_RMENU, VK_NUMLOCK, VK_SNAPSHOT, VK_CANCEL, VK_INSERT, VK_HOME, VK_PRIOR, VK_DELETE, VK_END, VK_NEXT, VK_LEFT, VK_DOWN, VK_RIGHT, VK_UP }; WORD keyCodes[nInputs] = { VK_RCONTROL, VK_RMENU, VK_NUMLOCK, VK_SNAPSHOT, VK_CANCEL, VK_INSERT, VK_HOME, VK_PRIOR, VK_DELETE, VK_END, VK_NEXT, VK_LEFT, VK_DOWN, VK_RIGHT, VK_UP };
@ -42,24 +42,22 @@ namespace RemappingLogicTests
for (int i = 0; i < nInputs; i++) for (int i = 0; i < nInputs; i++)
{ {
// Set key events for all the extended keys // Set key events for all the extended keys
Helpers::SetKeyEvent(input, i, INPUT_KEYBOARD, keyCodes[i], 0, 0); Helpers::SetKeyEvent(inputs, INPUT_KEYBOARD, keyCodes[i], 0, 0);
// Extended key flag should be set // Extended key flag should be set
Assert::AreEqual(true, bool(input[i].ki.dwFlags & KEYEVENTF_EXTENDEDKEY)); Assert::AreEqual(true, bool(inputs[i].ki.dwFlags & KEYEVENTF_EXTENDEDKEY));
} }
} }
// Test if SetKeyEvent sets the scan code field to 0 for dummy key // Test if SetKeyEvent sets the scan code field to 0 for dummy key
TEST_METHOD (SetKeyEvent_ShouldSetScanCodeFieldTo0_WhenArgumentIsDummyKey) TEST_METHOD (SetKeyEvent_ShouldSetScanCodeFieldTo0_WhenArgumentIsDummyKey)
{ {
const int nInputs = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE; std::vector<INPUT> inputs{};
INPUT input[nInputs] = {};
int index = 0; Helpers::SetDummyKeyEvent(inputs, 0);
Helpers::SetDummyKeyEvent(input, index, 0);
// Assert that wScan for both inputs is 0 // Assert that wScan for both inputs is 0
Assert::AreEqual<unsigned int>(0, input[0].ki.wScan); Assert::AreEqual<unsigned int>(0, inputs[0].ki.wScan);
Assert::AreEqual<unsigned int>(0, input[1].ki.wScan); Assert::AreEqual<unsigned int>(0, inputs[1].ki.wScan);
} }
}; };
} }

View File

@ -39,22 +39,24 @@ namespace RemappingLogicTests
{ {
// Remap A to B // Remap A to B
testState.AddSingleKeyRemap(0x41, (DWORD)0x42); testState.AddSingleKeyRemap(0x41, (DWORD)0x42);
const int nInputs = 1;
INPUT input[nInputs] = {}; std::vector<INPUT> inputs1{
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } },
input[0].ki.wVk = 0x41; };
// Send A keydown // Send A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs1);
// A key state should be unchanged, and B key state should be true // A key state should be unchanged, and B key state should be true
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
std::vector<INPUT> inputs2{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } },
};
// Send A keyup // Send A keyup
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs2);
// A key state should be unchanged, and B key state should be false // A key state should be unchanged, and B key state should be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
@ -66,21 +68,23 @@ namespace RemappingLogicTests
{ {
// Remap A to VK_DISABLE (disabled) // Remap A to VK_DISABLE (disabled)
testState.AddSingleKeyRemap(0x41, CommonSharedConstants::VK_DISABLED); testState.AddSingleKeyRemap(0x41, CommonSharedConstants::VK_DISABLED);
const int nInputs = 1;
INPUT input[nInputs] = {}; std::vector<INPUT> inputs1{
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } },
input[0].ki.wVk = 0x41; };
// Send A keydown // Send A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs1);
// A key state should be unchanged // A key state should be unchanged
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
std::vector<INPUT> inputs2{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } },
};
// Send A keyup // Send A keyup
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs2);
// A key state should be unchanged // A key state should be unchanged
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
@ -91,22 +95,24 @@ namespace RemappingLogicTests
{ {
// Remap A to Common Win key // Remap A to Common Win key
testState.AddSingleKeyRemap(0x41, CommonSharedConstants::VK_WIN_BOTH); testState.AddSingleKeyRemap(0x41, CommonSharedConstants::VK_WIN_BOTH);
const int nInputs = 1;
INPUT input[nInputs] = {}; std::vector<INPUT> inputs1{
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } },
input[0].ki.wVk = 0x41; };
// Send A keydown // Send A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs1);
// A key state should be unchanged, and common Win key state should be true // A key state should be unchanged, and common Win key state should be true
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LWIN), true); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LWIN), true);
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
std::vector<INPUT> inputs2{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } },
};
// Send A keyup // Send A keyup
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs2);
// A key state should be unchanged, and common Win key state should be false // A key state should be unchanged, and common Win key state should be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
@ -126,14 +132,13 @@ namespace RemappingLogicTests
// Remap Caps Lock to Ctrl // Remap Caps Lock to Ctrl
testState.AddSingleKeyRemap(VK_CAPITAL, (DWORD)VK_CONTROL); testState.AddSingleKeyRemap(VK_CAPITAL, (DWORD)VK_CONTROL);
const int nInputs = 1;
INPUT input[nInputs] = {}; std::vector<INPUT> inputs{
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CAPITAL } },
input[0].ki.wVk = VK_CAPITAL; };
// Send Caps Lock keydown // Send Caps Lock keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs);
// SendVirtualInput should be called exactly once with the above condition // SendVirtualInput should be called exactly once with the above condition
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount()); Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
@ -152,14 +157,13 @@ namespace RemappingLogicTests
// Remap Ctrl to Caps Lock // Remap Ctrl to Caps Lock
testState.AddSingleKeyRemap(VK_CONTROL, (DWORD)VK_CAPITAL); testState.AddSingleKeyRemap(VK_CONTROL, (DWORD)VK_CAPITAL);
const int nInputs = 1;
INPUT input[nInputs] = {}; std::vector<INPUT> inputs{
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
input[0].ki.wVk = VK_CONTROL; };
// Send Ctrl keydown // Send Ctrl keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs);
// SendVirtualInput should be called exactly once with the above condition // SendVirtualInput should be called exactly once with the above condition
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount()); Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
@ -182,14 +186,13 @@ namespace RemappingLogicTests
dest.SetKey(VK_SHIFT); dest.SetKey(VK_SHIFT);
dest.SetKey(0x56); dest.SetKey(0x56);
testState.AddSingleKeyRemap(VK_CAPITAL, dest); testState.AddSingleKeyRemap(VK_CAPITAL, dest);
const int nInputs = 1;
INPUT input[nInputs] = {}; std::vector<INPUT> inputs{
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CAPITAL } },
input[0].ki.wVk = VK_CAPITAL; };
// Send Caps Lock keydown // Send Caps Lock keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs);
// SendVirtualInput should be called exactly twice with the above condition // SendVirtualInput should be called exactly twice with the above condition
Assert::AreEqual(2, mockedInputHandler.GetSendVirtualInputCallCount()); Assert::AreEqual(2, mockedInputHandler.GetSendVirtualInputCallCount());
@ -211,14 +214,13 @@ namespace RemappingLogicTests
dest.SetKey(VK_CONTROL); dest.SetKey(VK_CONTROL);
dest.SetKey(VK_CAPITAL); dest.SetKey(VK_CAPITAL);
testState.AddSingleKeyRemap(VK_CONTROL, dest); testState.AddSingleKeyRemap(VK_CONTROL, dest);
const int nInputs = 1;
INPUT input[nInputs] = {}; std::vector<INPUT> inputs{
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
input[0].ki.wVk = VK_CONTROL; };
// Send Ctrl keydown // Send Ctrl keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs);
// SendVirtualInput should be called exactly once with the above condition // SendVirtualInput should be called exactly once with the above condition
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount()); Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
@ -232,23 +234,25 @@ namespace RemappingLogicTests
dest.SetKey(VK_CONTROL); dest.SetKey(VK_CONTROL);
dest.SetKey(0x56); dest.SetKey(0x56);
testState.AddSingleKeyRemap(0x41, dest); testState.AddSingleKeyRemap(0x41, dest);
const int nInputs = 1;
INPUT input[nInputs] = {}; std::vector<INPUT> inputs1{
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } },
input[0].ki.wVk = 0x41; };
// Send A keydown // Send A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs1);
// A key state should be unchanged, and Ctrl, V key state should be true // A key state should be unchanged, and Ctrl, V key state should be true
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true);
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
std::vector<INPUT> inputs2{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } },
};
// Send A keyup // Send A keyup
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs2);
// A key state should be unchanged, and Ctrl, V key state should be false // A key state should be unchanged, and Ctrl, V key state should be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
@ -265,24 +269,26 @@ namespace RemappingLogicTests
dest.SetKey(VK_SHIFT); dest.SetKey(VK_SHIFT);
dest.SetKey(0x56); dest.SetKey(0x56);
testState.AddSingleKeyRemap(0x41, dest); testState.AddSingleKeyRemap(0x41, dest);
const int nInputs = 1;
INPUT input[nInputs] = {}; std::vector<INPUT> inputs1{
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } },
input[0].ki.wVk = 0x41; };
// Send A keydown // Send A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs1);
// A key state should be unchanged, and Ctrl, Shift, V key state should be true // A key state should be unchanged, and Ctrl, Shift, V key state should be true
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), true); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), true);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true);
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
std::vector<INPUT> inputs2{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } },
};
// Send A keyup // Send A keyup
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs2);
// A key state should be unchanged, and Ctrl, Shift, V key state should be false // A key state should be unchanged, and Ctrl, Shift, V key state should be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
@ -299,22 +305,24 @@ namespace RemappingLogicTests
dest.SetKey(VK_LCONTROL); dest.SetKey(VK_LCONTROL);
dest.SetKey(0x56); dest.SetKey(0x56);
testState.AddSingleKeyRemap(VK_LCONTROL, dest); testState.AddSingleKeyRemap(VK_LCONTROL, dest);
const int nInputs = 1;
INPUT input[nInputs] = {}; std::vector<INPUT> inputs1{
input[0].type = INPUT_KEYBOARD; { .type = INPUT_KEYBOARD, .ki = { .wVk = VK_LCONTROL } },
input[0].ki.wVk = VK_LCONTROL; };
// Send LCtrl keydown // Send LCtrl keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs1);
// LCtrl, V key state should be true // LCtrl, V key state should be true
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LCONTROL), true); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LCONTROL), true);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true);
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
std::vector<INPUT> inputs2{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_LCONTROL, .dwFlags = KEYEVENTF_KEYUP } },
};
// Send LCtrl keyup // Send LCtrl keyup
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT)); mockedInputHandler.SendVirtualInput(inputs2);
// LCtrl, V key state should be false // LCtrl, V key state should be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LCONTROL), false); Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LCONTROL), false);

View File

@ -154,29 +154,29 @@ namespace Helpers
} }
// Function to set the value of a key event based on the arguments // Function to set the value of a key event based on the arguments
void SetKeyEvent(LPINPUT keyEventArray, int index, DWORD inputType, WORD keyCode, DWORD flags, ULONG_PTR extraInfo) void SetKeyEvent(std::vector<INPUT>& keyEventArray, DWORD inputType, WORD keyCode, DWORD flags, ULONG_PTR extraInfo)
{ {
keyEventArray[index].type = inputType; INPUT keyEvent{};
keyEventArray[index].ki.wVk = keyCode; keyEvent.type = inputType;
keyEventArray[index].ki.dwFlags = flags; keyEvent.ki.wVk = keyCode;
keyEvent.ki.dwFlags = flags;
if (IsExtendedKey(keyCode)) if (IsExtendedKey(keyCode))
{ {
keyEventArray[index].ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; keyEvent.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
} }
keyEventArray[index].ki.dwExtraInfo = extraInfo; keyEvent.ki.dwExtraInfo = extraInfo;
// Set wScan to the value from MapVirtualKey as some applications may use the scan code for handling input, for instance, Windows Terminal ignores non-character input which has scancode set to 0. // Set wScan to the value from MapVirtualKey as some applications may use the scan code for handling input, for instance, Windows Terminal ignores non-character input which has scancode set to 0.
// MapVirtualKey returns 0 if the key code does not correspond to a physical key (such as unassigned/reserved keys). More details at https://github.com/microsoft/PowerToys/pull/7143#issue-498877747 // MapVirtualKey returns 0 if the key code does not correspond to a physical key (such as unassigned/reserved keys). More details at https://github.com/microsoft/PowerToys/pull/7143#issue-498877747
keyEventArray[index].ki.wScan = static_cast<WORD>(MapVirtualKey(keyCode, MAPVK_VK_TO_VSC)); keyEvent.ki.wScan = static_cast<WORD>(MapVirtualKey(keyCode, MAPVK_VK_TO_VSC));
keyEventArray.push_back(keyEvent);
} }
// Function to set the dummy key events used for remapping shortcuts, required to ensure releasing a modifier doesn't trigger another action (For example, Win->Start Menu or Alt->Menu bar) // Function to set the dummy key events used for remapping shortcuts, required to ensure releasing a modifier doesn't trigger another action (For example, Win->Start Menu or Alt->Menu bar)
void SetDummyKeyEvent(LPINPUT keyEventArray, int& index, ULONG_PTR extraInfo) void SetDummyKeyEvent(std::vector<INPUT>& keyEventArray, ULONG_PTR extraInfo)
{ {
SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast<WORD>(KeyboardManagerConstants::DUMMY_KEY), 0, extraInfo); SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(KeyboardManagerConstants::DUMMY_KEY), 0, extraInfo);
index++; SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(KeyboardManagerConstants::DUMMY_KEY), KEYEVENTF_KEYUP, extraInfo);
SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast<WORD>(KeyboardManagerConstants::DUMMY_KEY), KEYEVENTF_KEYUP, extraInfo);
index++;
} }
// Function to return window handle for a full screen UWP app // Function to return window handle for a full screen UWP app
@ -240,7 +240,7 @@ namespace Helpers
} }
// Function to set key events for modifier keys: When shortcutToCompare is passed (non-empty shortcut), then the key event is sent only if both shortcut's don't have the same modifier key. When keyToBeReleased is passed (non-NULL), then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased // Function to set key events for modifier keys: When shortcutToCompare is passed (non-empty shortcut), then the key event is sent only if both shortcut's don't have the same modifier key. When keyToBeReleased is passed (non-NULL), then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
void SetModifierKeyEvents(const Shortcut& shortcutToBeSent, const ModifierKey& winKeyInvoked, LPINPUT keyEventArray, int& index, bool isKeyDown, ULONG_PTR extraInfoFlag, const Shortcut& shortcutToCompare, const DWORD& keyToBeReleased) void SetModifierKeyEvents(const Shortcut& shortcutToBeSent, const ModifierKey& winKeyInvoked, std::vector<INPUT>& keyEventArray, bool isKeyDown, ULONG_PTR extraInfoFlag, const Shortcut& shortcutToCompare, const DWORD& keyToBeReleased)
{ {
// If key down is to be sent, send in the order Win, Ctrl, Alt, Shift // If key down is to be sent, send in the order Win, Ctrl, Alt, Shift
if (isKeyDown) if (isKeyDown)
@ -248,23 +248,19 @@ namespace Helpers
// If shortcutToCompare is non-empty, then the key event is sent only if both shortcut's don't have the same modifier key. If keyToBeReleased is non-NULL, then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased // If shortcutToCompare is non-empty, then the key event is sent only if both shortcut's don't have the same modifier key. If keyToBeReleased is non-NULL, then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
if (shortcutToBeSent.GetWinKey(winKeyInvoked) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(winKeyInvoked) != shortcutToCompare.GetWinKey(winKeyInvoked)) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckWinKey(keyToBeReleased))) if (shortcutToBeSent.GetWinKey(winKeyInvoked) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(winKeyInvoked) != shortcutToCompare.GetWinKey(winKeyInvoked)) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckWinKey(keyToBeReleased)))
{ {
Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetWinKey(winKeyInvoked)), 0, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetWinKey(winKeyInvoked)), 0, extraInfoFlag);
index++;
} }
if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckCtrlKey(keyToBeReleased))) if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckCtrlKey(keyToBeReleased)))
{ {
Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetCtrlKey()), 0, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetCtrlKey()), 0, extraInfoFlag);
index++;
} }
if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckAltKey(keyToBeReleased))) if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckAltKey(keyToBeReleased)))
{ {
Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetAltKey()), 0, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetAltKey()), 0, extraInfoFlag);
index++;
} }
if (shortcutToBeSent.GetShiftKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckShiftKey(keyToBeReleased))) if (shortcutToBeSent.GetShiftKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckShiftKey(keyToBeReleased)))
{ {
Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetShiftKey()), 0, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetShiftKey()), 0, extraInfoFlag);
index++;
} }
} }
@ -274,23 +270,35 @@ namespace Helpers
// If shortcutToCompare is non-empty, then the key event is sent only if both shortcut's don't have the same modifier key. If keyToBeReleased is non-NULL, then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased // If shortcutToCompare is non-empty, then the key event is sent only if both shortcut's don't have the same modifier key. If keyToBeReleased is non-NULL, then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
if (shortcutToBeSent.GetShiftKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey() || shortcutToBeSent.CheckShiftKey(keyToBeReleased))) if (shortcutToBeSent.GetShiftKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey() || shortcutToBeSent.CheckShiftKey(keyToBeReleased)))
{ {
Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetShiftKey()), KEYEVENTF_KEYUP, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetShiftKey()), KEYEVENTF_KEYUP, extraInfoFlag);
index++;
} }
if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey() || shortcutToBeSent.CheckAltKey(keyToBeReleased))) if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey() || shortcutToBeSent.CheckAltKey(keyToBeReleased)))
{ {
Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetAltKey()), KEYEVENTF_KEYUP, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetAltKey()), KEYEVENTF_KEYUP, extraInfoFlag);
index++;
} }
if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey() || shortcutToBeSent.CheckCtrlKey(keyToBeReleased))) if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey() || shortcutToBeSent.CheckCtrlKey(keyToBeReleased)))
{ {
Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetCtrlKey()), KEYEVENTF_KEYUP, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetCtrlKey()), KEYEVENTF_KEYUP, extraInfoFlag);
index++;
} }
if (shortcutToBeSent.GetWinKey(winKeyInvoked) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(winKeyInvoked) != shortcutToCompare.GetWinKey(winKeyInvoked) || shortcutToBeSent.CheckWinKey(keyToBeReleased))) if (shortcutToBeSent.GetWinKey(winKeyInvoked) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(winKeyInvoked) != shortcutToCompare.GetWinKey(winKeyInvoked) || shortcutToBeSent.CheckWinKey(keyToBeReleased)))
{ {
Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetWinKey(winKeyInvoked)), KEYEVENTF_KEYUP, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetWinKey(winKeyInvoked)), KEYEVENTF_KEYUP, extraInfoFlag);
index++; }
}
}
void SetTextKeyEvents(std::vector<INPUT>& keyEventArray, const std::wstring& remapping)
{
for (wchar_t c : remapping)
{
for (DWORD flag : { 0, KEYEVENTF_KEYUP })
{
INPUT input{};
input.type = INPUT_KEYBOARD;
input.ki.dwFlags = KEYEVENTF_UNICODE | flag;
input.ki.dwExtraInfo = KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG;
input.ki.wScan = c;
keyEventArray.push_back(input);
} }
} }
} }

View File

@ -14,7 +14,7 @@ namespace Helpers
Shift, Shift,
Action Action
}; };
// Functions to encode that a key is originated from numpad // Functions to encode that a key is originated from numpad
DWORD EncodeKeyNumpadOrigin(const DWORD key, const bool extended); DWORD EncodeKeyNumpadOrigin(const DWORD key, const bool extended);
DWORD ClearKeyNumpadOrigin(const DWORD key); DWORD ClearKeyNumpadOrigin(const DWORD key);
@ -31,10 +31,13 @@ namespace Helpers
KeyType GetKeyType(DWORD key); KeyType GetKeyType(DWORD key);
// Function to set the value of a key event based on the arguments // Function to set the value of a key event based on the arguments
void SetKeyEvent(LPINPUT keyEventArray, int index, DWORD inputType, WORD keyCode, DWORD flags, ULONG_PTR extraInfo); void SetKeyEvent(std::vector<INPUT>& keyEventArray, DWORD inputType, WORD keyCode, DWORD flags, ULONG_PTR extraInfo);
// Function to set the dummy key events used for remapping shortcuts, required to ensure releasing a modifier doesn't trigger another action (For example, Win->Start Menu or Alt->Menu bar) // Function to set the dummy key events used for remapping shortcuts, required to ensure releasing a modifier doesn't trigger another action (For example, Win->Start Menu or Alt->Menu bar)
void SetDummyKeyEvent(LPINPUT keyEventArray, int& index, ULONG_PTR extraInfo); void SetDummyKeyEvent(std::vector<INPUT>& keyEventArray, ULONG_PTR extraInfo);
// Function to set key events for remapping text.
void SetTextKeyEvents(std::vector<INPUT>& keyEventArray, const std::wstring& remapping);
// Function to return window handle for a full screen UWP app // Function to return window handle for a full screen UWP app
HWND GetFullscreenUWPWindowHandle(); HWND GetFullscreenUWPWindowHandle();
@ -43,7 +46,7 @@ namespace Helpers
std::wstring GetCurrentApplication(bool keepPath); std::wstring GetCurrentApplication(bool keepPath);
// Function to set key events for modifier keys: When shortcutToCompare is passed (non-empty shortcut), then the key event is sent only if both shortcut's don't have the same modifier key. When keyToBeReleased is passed (non-NULL), then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased // Function to set key events for modifier keys: When shortcutToCompare is passed (non-empty shortcut), then the key event is sent only if both shortcut's don't have the same modifier key. When keyToBeReleased is passed (non-NULL), then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
void SetModifierKeyEvents(const Shortcut& shortcutToBeSent, const ModifierKey& winKeyInvoked, LPINPUT keyEventArray, int& index, bool isKeyDown, ULONG_PTR extraInfoFlag, const Shortcut& shortcutToCompare = Shortcut(), const DWORD& keyToBeReleased = NULL); void SetModifierKeyEvents(const Shortcut& shortcutToBeSent, const ModifierKey& winKeyInvoked, std::vector<INPUT>& keyEventArray, bool isKeyDown, ULONG_PTR extraInfoFlag, const Shortcut& shortcutToCompare = Shortcut(), const DWORD& keyToBeReleased = NULL);
// Function to filter the key codes for artificial key codes // Function to filter the key codes for artificial key codes
int32_t FilterArtificialKeys(const int32_t& key); int32_t FilterArtificialKeys(const int32_t& key);

View File

@ -1,7 +1,9 @@
#pragma once #pragma once
#include <keyboardmanager/common/InputInterface.h> #include <common/logger/logger.h>
#include <common/utils/winapi_error.h>
#include <keyboardmanager/common/Helpers.h> #include <keyboardmanager/common/Helpers.h>
#include <keyboardmanager/common/InputInterface.h>
namespace KeyboardManagerInput namespace KeyboardManagerInput
{ {
@ -10,9 +12,16 @@ namespace KeyboardManagerInput
{ {
public: public:
// Function to simulate input // Function to simulate input
UINT SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize) void SendVirtualInput(const std::vector<INPUT>& inputs)
{ {
return SendInput(cInputs, pInputs, cbSize); std::vector<INPUT> copy = inputs;
UINT eventCount = SendInput(static_cast<UINT>(copy.size()), copy.data(), sizeof(INPUT));
if (eventCount != copy.size())
{
Logger::error(
L"Failed to send input events. {}",
get_last_error_or_default(GetLastError()));
}
} }
// Function to get the state of a particular key // Function to get the state of a particular key

View File

@ -1,5 +1,9 @@
#pragma once #pragma once
#include <string>
#include <vector>
#include <Windows.h>
namespace KeyboardManagerInput namespace KeyboardManagerInput
{ {
// Interface used to wrap keyboard input library methods // Interface used to wrap keyboard input library methods
@ -7,7 +11,7 @@ namespace KeyboardManagerInput
{ {
public: public:
// Function to simulate input // Function to simulate input
virtual UINT SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize) = 0; virtual void SendVirtualInput(const std::vector<INPUT>& inputs) = 0;
// Function to get the state of a particular key // Function to get the state of a particular key
virtual bool GetVirtualKeyState(int key) = 0; virtual bool GetVirtualKeyState(int key) = 0;

View File

@ -10,14 +10,11 @@ namespace KeyboardEventHandlers
void SetNumLockToPreviousState(KeyboardManagerInput::InputInterface& ii) void SetNumLockToPreviousState(KeyboardManagerInput::InputInterface& ii)
{ {
// Num Lock's key state is applied before it is intercepted by low level keyboard hooks, so we have to manually set back the state when we suppress the key. This is done by sending an additional key up, key down set of messages. // Num Lock's key state is applied before it is intercepted by low level keyboard hooks, so we have to manually set back the state when we suppress the key. This is done by sending an additional key up, key down set of messages.
// We need 2 key events because after Num Lock is suppressed, key up to release num lock key and key down to revert the num lock state std::vector<INPUT> keyEventList;
int key_count = 2;
LPINPUT keyEventList = new INPUT[size_t(key_count)]{};
// Use the suppress 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
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, VK_NUMLOCK, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, VK_NUMLOCK, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
Helpers::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, VK_NUMLOCK, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, VK_NUMLOCK, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT)); ii.SendVirtualInput(keyEventList);
delete[] keyEventList;
} }
} }