[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.
[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 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).

View File

@ -69,7 +69,7 @@ namespace KeyboardEventHandlers
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
DWORD target;
@ -92,35 +92,31 @@ namespace KeyboardEventHandlers
{
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
{
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
{
int i = 0;
Shortcut targetShortcut = std::get<Shortcut>(it->second);
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);
i++;
Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(targetShortcut.GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, 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
}
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
Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(targetShortcut.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
i++;
Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(targetShortcut.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
}
}
UINT res = ii.SendVirtualInput(key_count, keyEventList, sizeof(INPUT));
delete[] keyEventList;
ii.SendVirtualInput(keyEventList);
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
{
@ -194,14 +190,12 @@ namespace KeyboardEventHandlers
return 1;
}
}
int key_count = 2;
LPINPUT keyEventList = new INPUT[size_t(key_count)]();
Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
Helpers::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
std::vector<INPUT> keyEventList;
Helpers::SetKeyEvent(keyEventList, 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);
lock.unlock();
UINT res = ii.SendVirtualInput(key_count, keyEventList, sizeof(INPUT));
delete[] keyEventList;
ii.SendVirtualInput(keyEventList);
// Reset the long press flag when the key has been lifted.
if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
@ -306,8 +300,7 @@ namespace KeyboardEventHandlers
continue;
}
size_t key_count = 0;
LPINPUT keyEventList = nullptr;
std::vector<INPUT> keyEventList;
// Remember which win key was pressed initially
if (ii.GetVirtualKeyState(VK_RWIN))
@ -331,8 +324,6 @@ namespace KeyboardEventHandlers
myThread.detach();
}
Logger::trace(L"ChordKeyboardHandler:returning..");
return 1;
}
@ -362,7 +353,6 @@ namespace KeyboardEventHandlers
}
}
auto threadFunction = [newUri]() {
HINSTANCE result = ShellExecute(NULL, L"open", newUri.c_str(), NULL, NULL, SW_SHOWNORMAL);
@ -380,7 +370,6 @@ namespace KeyboardEventHandlers
myThread.detach();
}
Logger::trace(L"ChordKeyboardHandler:returning..");
return 1;
}
@ -394,30 +383,22 @@ namespace KeyboardEventHandlers
if (commonKeys == src_size - 1)
{
// key down for all new shortcut keys except the common modifiers
key_count = dest_size - commonKeys;
keyEventList = new INPUT[key_count]{};
int i = 0;
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++;
keyEventList = std::vector<INPUT>{};
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
}
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
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
int i = 0;
Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// 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
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++;
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
}
// 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)
{
// 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
if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
{
key_count--;
// Since the original shortcut's action key is pressed, set it to true
it->second.isOriginalActionKeyPressed = true;
}
keyEventList = new INPUT[key_count]{};
// 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, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// 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
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);
i++;
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
}
// 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
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);
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);
Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// 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)
{
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];
}
Helpers::SetTextKeyEvents(keyEventList, remapping);
}
it->second.isShortcutInvoked = true;
@ -500,12 +458,9 @@ namespace KeyboardEventHandlers
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));
delete[] keyEventList;
// Send daily telemetry event for Keyboard Manager key activation.
ii.SendVirtualInput(keyEventList);
if (activatedApp.has_value())
{
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))
{
// Release new shortcut, and set original shortcut keys except the one released
size_t key_count = 0;
LPINPUT keyEventList = nullptr;
std::vector<INPUT> keyEventList;
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
bool isActionKeyPressed = false;
if (ii.GetVirtualKeyState((std::get<Shortcut>(it->second.targetShortcut).GetActionKey())))
{
isActionKeyPressed = true;
key_count += 1;
}
keyEventList = new INPUT[key_count]{};
bool isActionKeyPressed = ii.GetVirtualKeyState(std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
// Release new shortcut state (release in reverse order of shortcut to be accurate)
int i = 0;
if (isActionKeyPressed)
{
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
}
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
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
Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
}
else if (remapToKey)
{
// 1 for releasing new key and original shortcut modifiers except the one released and dummy key
key_count = dest_size + src_size - 2 + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
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]{};
bool isTargetKeyPressed = (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED) && ii.GetVirtualKeyState(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)));
// Release new key state
int i = 0;
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);
i++;
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
}
// 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
Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
}
// Reset the remap state
@ -665,12 +577,7 @@ namespace KeyboardEventHandlers
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
if (key_count > 0)
{
UINT res = ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT));
delete[] keyEventList;
}
ii.SendVirtualInput(keyEventList);
return 1;
}
@ -678,7 +585,7 @@ namespace KeyboardEventHandlers
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)
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
if (remapToKey && std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
@ -688,69 +595,46 @@ namespace KeyboardEventHandlers
return 1;
}
size_t key_count = 1;
LPINPUT keyEventList = nullptr;
std::vector<INPUT> keyEventList;
if (remapToShortcut)
{
keyEventList = new INPUT[key_count]{};
Helpers::SetKeyEvent(keyEventList, 0, 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);
}
else if (remapToKey)
{
keyEventList = new INPUT[key_count]{};
Helpers::SetKeyEvent(keyEventList, 0, 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);
}
else if (remapToText)
{
auto remapping = std::get<std::wstring>(it->second.targetShortcut);
const UINT inputEventCount = static_cast<UINT>(remapping.length() * 2);
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];
}
auto& remapping = std::get<std::wstring>(it->second.targetShortcut);
Helpers::SetTextKeyEvents(keyEventList, remapping);
}
UINT res = ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT));
delete[] keyEventList;
ii.SendVirtualInput(keyEventList);
return 1;
}
// Case 3: If the action key is released from the original shortcut, keep modifiers of the new shortcut until some other key event which doesn't apply to the original shortcut
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;
LPINPUT keyEventList = nullptr;
std::vector<INPUT> keyEventList;
if (remapToShortcut && !it->first.HasChord())
{
// Just lift the action key for no chords.
keyEventList = new INPUT[key_count]{};
Helpers::SetKeyEvent(keyEventList, 0, 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);
}
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.
// 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));
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++;
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// 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
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
it->second.isShortcutInvoked = false;
@ -762,7 +646,6 @@ namespace KeyboardEventHandlers
{
state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
}
}
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 (isKeyboardStateClear)
{
keyEventList = new INPUT[key_count]{};
Helpers::SetKeyEvent(keyEventList, 0, 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);
}
else
{
// 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
// 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
int i = 0;
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// 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
Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// Reset the remap state
it->second.isShortcutInvoked = false;
@ -816,8 +691,7 @@ namespace KeyboardEventHandlers
}
}
UINT res = ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT));
delete[] keyEventList;
ii.SendVirtualInput(keyEventList);
return 1;
}
@ -854,8 +728,7 @@ namespace KeyboardEventHandlers
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
}
size_t key_count;
LPINPUT keyEventList = nullptr;
std::vector<INPUT> keyEventList;
// Check if a new remapping should be applied
Shortcut currentlyPressed = it->first;
@ -868,40 +741,25 @@ namespace KeyboardEventHandlers
if (newRemapping.RemapToKey())
{
DWORD to = std::get<0>(newRemapping.targetShortcut);
bool isLastKeyStillPressed = ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey));
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);
Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
if (ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey)))
{
// 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);
i++;
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(from.actionKey), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
}
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
{
Shortcut to = std::get<Shortcut>(newRemapping.targetShortcut);
bool isLastKeyStillPressed = ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey));
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);
Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, to);
if (ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey)))
{
// 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);
i++;
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(from.actionKey), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
}
Helpers::SetModifierKeyEvents(to, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, from);
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(to.actionKey), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Helpers::SetModifierKeyEvents(to, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, from);
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(to.actionKey), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
newRemapping.isShortcutInvoked = true;
}
@ -917,41 +775,27 @@ namespace KeyboardEventHandlers
}
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
bool isActionKeyPressed = false;
if (ii.GetVirtualKeyState((std::get<Shortcut>(it->second.targetShortcut).GetActionKey())))
{
isActionKeyPressed = true;
key_count += 2;
}
keyEventList = new INPUT[key_count]{};
bool isActionKeyPressed = ii.GetVirtualKeyState(std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
// Release new shortcut state (release in reverse order of shortcut to be accurate)
int i = 0;
if (isActionKeyPressed)
{
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
}
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
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
if (isActionKeyPressed)
{
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(it->first.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(it->first.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
}
// 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);
i++;
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(data->lParam->vkCode), 0, 0);
// 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);
}
UINT res = ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT));
delete[] keyEventList;
ii.SendVirtualInput(keyEventList);
return 1;
}
else
@ -1003,30 +846,20 @@ namespace KeyboardEventHandlers
if (isRemapToDisable || !isOriginalActionKeyPressed)
{
// Key down for original shortcut modifiers and action key, and current key press
size_t key_count = src_size + 1;
LPINPUT keyEventList = new INPUT[key_count]{};
std::vector<INPUT> keyEventList;
// Set original shortcut key down state
int i = 0;
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 the original action key only if it is physically pressed. For remappings to keys other than disabled we already check earlier that it is not pressed in this scenario. For remap to disable
if (isRemapToDisable && isOriginalActionKeyPressed)
{
// Set original action key
Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast<WORD>(it->first.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++;
}
else
{
key_count--;
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(it->first.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
}
// 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);
i++;
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(data->lParam->vkCode), 0, 0);
// 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);
}
UINT res = ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT));
delete[] keyEventList;
ii.SendVirtualInput(keyEventList);
return 1;
}
else
@ -1344,7 +1176,7 @@ namespace KeyboardEventHandlers
if (targetPid != 0 && shortcut.alreadyRunningAction != Shortcut::ProgramAlreadyRunningAction::StartAnother)
{
if (shortcut.alreadyRunningAction == Shortcut::ProgramAlreadyRunningAction::EndTask)
if (shortcut.alreadyRunningAction == Shortcut::ProgramAlreadyRunningAction::EndTask)
{
TerminateProcessesByName(fileNamePart);
return;
@ -1803,13 +1635,11 @@ namespace KeyboardEventHandlers
// 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))
{
int key_count = 1;
LPINPUT keyEventList = new INPUT[size_t(key_count)]{};
std::vector<INPUT> keyEventList;
// 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);
UINT res = ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT));
delete[] keyEventList;
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(key), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
ii.SendVirtualInput(keyEventList);
}
}
}
@ -1834,21 +1664,9 @@ namespace KeyboardEventHandlers
return 0;
}
const size_t keyCount = remapping->length();
const UINT eventCount = static_cast<UINT>(keyCount * 2);
auto keyEventList = std::make_unique<INPUT[]>(keyCount * 2);
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));
std::vector<INPUT> keyEventList;
Helpers::SetTextKeyEvents(keyEventList, *remapping);
ii.SendVirtualInput(keyEventList);
return 1;
}

View File

@ -51,15 +51,13 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
std::vector<INPUT> inputs{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
};
// 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
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
@ -83,15 +81,13 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp2);
const int nInputs = 2;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
std::vector<INPUT> inputs{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
};
// 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
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
@ -115,28 +111,24 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
std::vector<INPUT> inputs1{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
};
// Send Ctrl+A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs1);
// Activated app should be testApp1
Assert::AreEqual(testApp1, testState.GetActivatedApp());
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0x41;
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = VK_CONTROL;
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
std::vector<INPUT> inputs2{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } },
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL, .dwFlags = KEYEVENTF_KEYUP } }
};
// Release A then Ctrl
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs2);
// Activated app should be empty string
Assert::AreEqual(std::wstring(KeyboardManagerConstants::NoActivatedApp), testState.GetActivatedApp());
@ -156,28 +148,24 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
std::vector<INPUT> inputs1{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
};
// Send Ctrl+A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs1);
// Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp2);
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0x41;
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = VK_CONTROL;
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
std::vector<INPUT> inputs2{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } },
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL, .dwFlags = KEYEVENTF_KEYUP } }
};
// Release A then Ctrl
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs2);
// Ctrl, A, Alt and Tab should all be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
@ -198,15 +186,13 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
std::vector<INPUT> inputs{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
};
// 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
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
@ -226,15 +212,13 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp2);
const int nInputs = 2;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
std::vector<INPUT> inputs{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
};
// 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
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
@ -254,28 +238,24 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
std::vector<INPUT> inputs1{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
};
// Send Ctrl+A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs1);
// Activated app should be testApp1
Assert::AreEqual(testApp1, testState.GetActivatedApp());
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0x41;
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = VK_CONTROL;
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
std::vector<INPUT> inputs2{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } },
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL, .dwFlags = KEYEVENTF_KEYUP } }
};
// Release A then Ctrl
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs2);
// Activated app should be empty string
Assert::AreEqual(std::wstring(KeyboardManagerConstants::NoActivatedApp), testState.GetActivatedApp());
@ -292,28 +272,24 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x41;
std::vector<INPUT> inputs1{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } }
};
// Send Ctrl+A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs1);
// Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp2);
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0x41;
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = VK_CONTROL;
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
std::vector<INPUT> inputs2{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A', .dwFlags = KEYEVENTF_KEYUP } },
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL, .dwFlags = KEYEVENTF_KEYUP } }
};
// Release A then Ctrl
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs2);
// Ctrl, A, V should all be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
@ -334,15 +310,13 @@ namespace RemappingLogicTests
// Set the testApp as the foreground process
mockedInputHandler.SetForegroundProcess(testApp1);
const int nInputs = 2;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = actionKey;
std::vector<INPUT> inputs{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
{ .type = INPUT_KEYBOARD, .ki = { .wVk = actionKey } }
};
// 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
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)
UINT MockedInput::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int /*cbSize*/)
void MockedInput::SendVirtualInput(const std::vector<INPUT>& 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
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
if (input.ki.dwFlags & KEYEVENTF_KEYUP)
{
if (keyboardState[VK_MENU] == true)
{
@ -31,7 +31,7 @@ UINT MockedInput::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int /*cbSize*/
}
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;
}
@ -43,8 +43,8 @@ UINT MockedInput::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int /*cbSize*/
KBDLLHOOKSTRUCT lParam = {};
// Set only vkCode and dwExtraInfo since other values are unused
lParam.vkCode = pInputs[i].ki.wVk;
lParam.dwExtraInfo = pInputs[i].ki.dwExtraInfo;
lParam.vkCode = input.ki.wVk;
lParam.dwExtraInfo = input.ki.dwExtraInfo;
keyEvent.lParam = &lParam;
// If the SendVirtualInput call condition is true, increment the count. If no condition is set then always increment the count
@ -60,55 +60,53 @@ UINT MockedInput::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int /*cbSize*/
if (result == 0)
{
// If key up flag is set, then set keyboard state to false
keyboardState[pInputs[i].ki.wVk] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
keyboardState[input.ki.wVk] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
// Handling modifier key codes
switch (pInputs[i].ki.wVk)
switch (input.ki.wVk)
{
case VK_CONTROL:
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
if (input.ki.dwFlags & KEYEVENTF_KEYUP)
{
keyboardState[VK_LCONTROL] = false;
keyboardState[VK_RCONTROL] = false;
}
break;
case VK_LCONTROL:
keyboardState[VK_CONTROL] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
keyboardState[VK_CONTROL] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break;
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;
case VK_MENU:
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
if (input.ki.dwFlags & KEYEVENTF_KEYUP)
{
keyboardState[VK_LMENU] = false;
keyboardState[VK_RMENU] = false;
}
break;
case VK_LMENU:
keyboardState[VK_MENU] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
keyboardState[VK_MENU] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break;
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;
case VK_SHIFT:
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
if (input.ki.dwFlags & KEYEVENTF_KEYUP)
{
keyboardState[VK_LSHIFT] = false;
keyboardState[VK_RSHIFT] = false;
}
break;
case VK_LSHIFT:
keyboardState[VK_SHIFT] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
keyboardState[VK_SHIFT] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break;
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;
}
}
}
return cInputs;
}
// Function to simulate keyboard hook behavior

View File

@ -34,7 +34,7 @@ namespace KeyboardManagerInput
void SetHookProc(std::function<intptr_t(LowlevelKeyboardEvent*)> hookProcedure);
// 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
intptr_t MockedKeyboardHook(LowlevelKeyboardEvent* data);

View File

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

View File

@ -34,7 +34,7 @@ namespace RemappingLogicTests
TEST_METHOD (SetKeyEvent_ShouldUseExtendedKeyFlag_WhenArgumentIsExtendedKey)
{
const int nInputs = 15;
INPUT input[nInputs] = {};
std::vector<INPUT> inputs;
// 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 };
@ -42,24 +42,22 @@ namespace RemappingLogicTests
for (int i = 0; i < nInputs; i++)
{
// 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
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_METHOD (SetKeyEvent_ShouldSetScanCodeFieldTo0_WhenArgumentIsDummyKey)
{
const int nInputs = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
INPUT input[nInputs] = {};
std::vector<INPUT> inputs{};
int index = 0;
Helpers::SetDummyKeyEvent(input, index, 0);
Helpers::SetDummyKeyEvent(inputs, 0);
// Assert that wScan for both inputs is 0
Assert::AreEqual<unsigned int>(0, input[0].ki.wScan);
Assert::AreEqual<unsigned int>(0, input[1].ki.wScan);
Assert::AreEqual<unsigned int>(0, inputs[0].ki.wScan);
Assert::AreEqual<unsigned int>(0, inputs[1].ki.wScan);
}
};
}

View File

@ -39,22 +39,24 @@ namespace RemappingLogicTests
{
// Remap A to B
testState.AddSingleKeyRemap(0x41, (DWORD)0x42);
const int nInputs = 1;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0x41;
std::vector<INPUT> inputs1{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } },
};
// 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
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
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
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs2);
// A key state should be unchanged, and B key state should be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
@ -66,21 +68,23 @@ namespace RemappingLogicTests
{
// Remap A to VK_DISABLE (disabled)
testState.AddSingleKeyRemap(0x41, CommonSharedConstants::VK_DISABLED);
const int nInputs = 1;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0x41;
std::vector<INPUT> inputs1{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } },
};
// Send A keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs1);
// A key state should be unchanged
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
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs2);
// A key state should be unchanged
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
@ -91,22 +95,24 @@ namespace RemappingLogicTests
{
// Remap A to Common Win key
testState.AddSingleKeyRemap(0x41, CommonSharedConstants::VK_WIN_BOTH);
const int nInputs = 1;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0x41;
std::vector<INPUT> inputs1{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } },
};
// 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
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
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
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs2);
// A key state should be unchanged, and common Win key state should be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
@ -126,14 +132,13 @@ namespace RemappingLogicTests
// Remap Caps Lock to Ctrl
testState.AddSingleKeyRemap(VK_CAPITAL, (DWORD)VK_CONTROL);
const int nInputs = 1;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CAPITAL;
std::vector<INPUT> inputs{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CAPITAL } },
};
// Send Caps Lock keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs);
// SendVirtualInput should be called exactly once with the above condition
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
@ -152,14 +157,13 @@ namespace RemappingLogicTests
// Remap Ctrl to Caps Lock
testState.AddSingleKeyRemap(VK_CONTROL, (DWORD)VK_CAPITAL);
const int nInputs = 1;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
std::vector<INPUT> inputs{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
};
// Send Ctrl keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs);
// SendVirtualInput should be called exactly once with the above condition
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
@ -182,14 +186,13 @@ namespace RemappingLogicTests
dest.SetKey(VK_SHIFT);
dest.SetKey(0x56);
testState.AddSingleKeyRemap(VK_CAPITAL, dest);
const int nInputs = 1;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CAPITAL;
std::vector<INPUT> inputs{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CAPITAL } },
};
// Send Caps Lock keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs);
// SendVirtualInput should be called exactly twice with the above condition
Assert::AreEqual(2, mockedInputHandler.GetSendVirtualInputCallCount());
@ -211,14 +214,13 @@ namespace RemappingLogicTests
dest.SetKey(VK_CONTROL);
dest.SetKey(VK_CAPITAL);
testState.AddSingleKeyRemap(VK_CONTROL, dest);
const int nInputs = 1;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_CONTROL;
std::vector<INPUT> inputs{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_CONTROL } },
};
// Send Ctrl keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs);
// SendVirtualInput should be called exactly once with the above condition
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
@ -232,23 +234,25 @@ namespace RemappingLogicTests
dest.SetKey(VK_CONTROL);
dest.SetKey(0x56);
testState.AddSingleKeyRemap(0x41, dest);
const int nInputs = 1;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0x41;
std::vector<INPUT> inputs1{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } },
};
// 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
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), 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
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs2);
// A key state should be unchanged, and Ctrl, V key state should be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
@ -265,24 +269,26 @@ namespace RemappingLogicTests
dest.SetKey(VK_SHIFT);
dest.SetKey(0x56);
testState.AddSingleKeyRemap(0x41, dest);
const int nInputs = 1;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0x41;
std::vector<INPUT> inputs1{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = 'A' } },
};
// 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
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), 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
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs2);
// A key state should be unchanged, and Ctrl, Shift, V key state should be false
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
@ -299,22 +305,24 @@ namespace RemappingLogicTests
dest.SetKey(VK_LCONTROL);
dest.SetKey(0x56);
testState.AddSingleKeyRemap(VK_LCONTROL, dest);
const int nInputs = 1;
INPUT input[nInputs] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_LCONTROL;
std::vector<INPUT> inputs1{
{ .type = INPUT_KEYBOARD, .ki = { .wVk = VK_LCONTROL } },
};
// Send LCtrl keydown
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs1);
// LCtrl, V key state should be true
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LCONTROL), 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
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
mockedInputHandler.SendVirtualInput(inputs2);
// LCtrl, V key state should be 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
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;
keyEventArray[index].ki.wVk = keyCode;
keyEventArray[index].ki.dwFlags = flags;
INPUT keyEvent{};
keyEvent.type = inputType;
keyEvent.ki.wVk = keyCode;
keyEvent.ki.dwFlags = flags;
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.
// 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)
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);
index++;
SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast<WORD>(KeyboardManagerConstants::DUMMY_KEY), KEYEVENTF_KEYUP, extraInfo);
index++;
SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(KeyboardManagerConstants::DUMMY_KEY), 0, extraInfo);
SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(KeyboardManagerConstants::DUMMY_KEY), KEYEVENTF_KEYUP, extraInfo);
}
// 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
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 (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 (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);
index++;
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetWinKey(winKeyInvoked)), 0, extraInfoFlag);
}
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);
index++;
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetCtrlKey()), 0, extraInfoFlag);
}
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);
index++;
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetAltKey()), 0, extraInfoFlag);
}
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);
index++;
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetShiftKey()), 0, extraInfoFlag);
}
}
@ -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 (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);
index++;
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetShiftKey()), KEYEVENTF_KEYUP, extraInfoFlag);
}
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);
index++;
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetAltKey()), KEYEVENTF_KEYUP, extraInfoFlag);
}
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);
index++;
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetCtrlKey()), KEYEVENTF_KEYUP, extraInfoFlag);
}
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);
index++;
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetWinKey(winKeyInvoked)), KEYEVENTF_KEYUP, extraInfoFlag);
}
}
}
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,
Action
};
// Functions to encode that a key is originated from numpad
DWORD EncodeKeyNumpadOrigin(const DWORD key, const bool extended);
DWORD ClearKeyNumpadOrigin(const DWORD key);
@ -31,10 +31,13 @@ namespace Helpers
KeyType GetKeyType(DWORD key);
// 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)
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
HWND GetFullscreenUWPWindowHandle();
@ -43,7 +46,7 @@ namespace Helpers
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
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
int32_t FilterArtificialKeys(const int32_t& key);

View File

@ -1,7 +1,9 @@
#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/InputInterface.h>
namespace KeyboardManagerInput
{
@ -10,9 +12,16 @@ namespace KeyboardManagerInput
{
public:
// 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

View File

@ -1,5 +1,9 @@
#pragma once
#include <string>
#include <vector>
#include <Windows.h>
namespace KeyboardManagerInput
{
// Interface used to wrap keyboard input library methods
@ -7,7 +11,7 @@ namespace KeyboardManagerInput
{
public:
// 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
virtual bool GetVirtualKeyState(int key) = 0;

View File

@ -10,14 +10,11 @@ namespace KeyboardEventHandlers
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.
// 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
int key_count = 2;
LPINPUT keyEventList = new INPUT[size_t(key_count)]{};
std::vector<INPUT> keyEventList;
// 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, 1, INPUT_KEYBOARD, VK_NUMLOCK, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
ii.SendVirtualInput(static_cast<UINT>(key_count), keyEventList, sizeof(INPUT));
delete[] keyEventList;
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, VK_NUMLOCK, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, VK_NUMLOCK, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
ii.SendVirtualInput(keyEventList);
}
}