diff --git a/src/modules/keyboardmanager/common/Helpers.cpp b/src/modules/keyboardmanager/common/Helpers.cpp index 7aa5642960..07f2cdb059 100644 --- a/src/modules/keyboardmanager/common/Helpers.cpp +++ b/src/modules/keyboardmanager/common/Helpers.cpp @@ -2,6 +2,7 @@ #include "Helpers.h" #include #include "../common/shared_constants.h" +#include using namespace winrt::Windows::Foundation; @@ -163,4 +164,63 @@ namespace KeyboardManagerHelper return L"Unexpected error"; } } + + // 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) + { + keyEventArray[index].type = inputType; + keyEventArray[index].ki.wVk = keyCode; + keyEventArray[index].ki.dwFlags = flags; + if (IsExtendedKey(keyCode)) + { + keyEventArray[index].ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; + } + keyEventArray[index].ki.dwExtraInfo = extraInfo; + } + + // Function to return the window in focus + HWND GetFocusWindowHandle() + { + // Using GetGUIThreadInfo for getting the process of the window in focus. GetForegroundWindow has issues with UWP apps as it returns the Application Frame Host as its linked process + GUITHREADINFO guiThreadInfo; + guiThreadInfo.cbSize = sizeof(GUITHREADINFO); + GetGUIThreadInfo(0, &guiThreadInfo); + + // If no window in focus, use the active window + if (guiThreadInfo.hwndFocus == nullptr) + { + return guiThreadInfo.hwndActive; + } + return guiThreadInfo.hwndFocus; + } + + // Function to return the executable name of the application in focus + std::wstring GetCurrentApplication(bool keepPath) + { + HWND current_window_handle = GetFocusWindowHandle(); + DWORD process_id; + DWORD nSize = MAX_PATH; + WCHAR buffer[MAX_PATH] = { 0 }; + + // Get process ID of the focus window + DWORD thread_id = GetWindowThreadProcessId(current_window_handle, &process_id); + HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_id); + + // Get full path of the executable + bool res = QueryFullProcessImageName(hProc, 0, buffer, &nSize); + std::wstring process_name; + CloseHandle(hProc); + + process_name = buffer; + if (res) + { + PathStripPath(buffer); + + if (!keepPath) + { + process_name = buffer; + } + } + return process_name; + } } diff --git a/src/modules/keyboardmanager/common/Helpers.h b/src/modules/keyboardmanager/common/Helpers.h index bc9b65d0b1..0e61f6766a 100644 --- a/src/modules/keyboardmanager/common/Helpers.h +++ b/src/modules/keyboardmanager/common/Helpers.h @@ -69,4 +69,13 @@ namespace KeyboardManagerHelper // Function to return the list of key name in the order for the drop down based on the key codes winrt::Windows::Foundation::Collections::IVector ToBoxValue(const std::vector& list); + + // 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); + + // Function to return the window in focus + HWND GetFocusWindowHandle(); + + // Function to return the executable name of the application in focus + std::wstring GetCurrentApplication(bool keepPath); } \ No newline at end of file diff --git a/src/modules/keyboardmanager/common/KeyboardManagerCommon.vcxproj b/src/modules/keyboardmanager/common/KeyboardManagerCommon.vcxproj index c860fed9e7..e64274999b 100644 --- a/src/modules/keyboardmanager/common/KeyboardManagerCommon.vcxproj +++ b/src/modules/keyboardmanager/common/KeyboardManagerCommon.vcxproj @@ -63,6 +63,8 @@ stdcpplatest ..\;..\..\..\common;..\..\..\common\telemetry;..\..\;%(AdditionalIncludeDirectories) MultiThreadedDebug + true + 4002 Console @@ -80,6 +82,8 @@ stdcpplatest ..\;..\..\..\common;..\..\..\common\telemetry;..\..\;%(AdditionalIncludeDirectories) MultiThreaded + true + 4002 Console diff --git a/src/modules/keyboardmanager/common/KeyboardManagerConstants.h b/src/modules/keyboardmanager/common/KeyboardManagerConstants.h index 261d74ae4f..3f849d6160 100644 --- a/src/modules/keyboardmanager/common/KeyboardManagerConstants.h +++ b/src/modules/keyboardmanager/common/KeyboardManagerConstants.h @@ -76,4 +76,12 @@ namespace KeyboardManagerConstants // Shared style constants for both Remap Table and Shortcut Table inline const double HeaderButtonWidth = 100; + + + // Flags used for distinguishing key events sent by Keyboard Manager + inline const ULONG_PTR KEYBOARDMANAGER_SINGLEKEY_FLAG = 0x11; + inline const ULONG_PTR KEYBOARDMANAGER_SHORTCUT_FLAG = 0x101; + + // Dummy key event used in between key up and down events to prevent certain global events from happening + inline const DWORD DUMMY_KEY = 0xFF; } \ No newline at end of file diff --git a/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp b/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp new file mode 100644 index 0000000000..e78b260490 --- /dev/null +++ b/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp @@ -0,0 +1,488 @@ +#include "pch.h" +#include "KeyboardEventHandlers.h" + +namespace KeyboardEventHandlers +{ + // Function to a handle a single key remap + intptr_t HandleSingleKeyRemapEvent(LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept + { + // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. + if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG)) + { + // The mutex should be unlocked before SendInput is called to avoid re-entry into the same mutex. More details can be found at https://github.com/microsoft/PowerToys/pull/1789#issuecomment-607555837 + std::unique_lock lock(keyboardManagerState.singleKeyReMap_mutex); + auto it = keyboardManagerState.singleKeyReMap.find(data->lParam->vkCode); + if (it != keyboardManagerState.singleKeyReMap.end()) + { + // If mapped to 0x0 then the key is disabled + if (it->second == 0x0) + { + return 1; + } + + int key_count = 1; + LPINPUT keyEventList = new INPUT[size_t(key_count)](); + memset(keyEventList, 0, sizeof(keyEventList)); + + // Handle remaps to VK_WIN_BOTH + DWORD target = it->second; + // If a key is remapped to VK_WIN_BOTH, we send VK_LWIN instead + if (target == CommonSharedConstants::VK_WIN_BOTH) + { + target = VK_LWIN; + } + + if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); + } + else + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); + } + + lock.unlock(); + UINT res = SendInput(key_count, keyEventList, sizeof(INPUT)); + delete[] keyEventList; + return 1; + } + } + + return 0; + } + + // Function to a change a key's behavior from toggle to modifier + intptr_t HandleSingleKeyToggleToModEvent(LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept + { + // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. + if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG)) + { + // The mutex should be unlocked before SendInput is called to avoid re-entry into the same mutex. More details can be found at https://github.com/microsoft/PowerToys/pull/1789#issuecomment-607555837 + std::unique_lock lock(keyboardManagerState.singleKeyToggleToMod_mutex); + auto it = keyboardManagerState.singleKeyToggleToMod.find(data->lParam->vkCode); + if (it != keyboardManagerState.singleKeyToggleToMod.end()) + { + // To avoid long presses (which leads to continuous keydown messages) from toggling the key on and off + if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN) + { + if (it->second == false) + { + keyboardManagerState.singleKeyToggleToMod[data->lParam->vkCode] = true; + } + else + { + lock.unlock(); + return 1; + } + } + int key_count = 2; + LPINPUT keyEventList = new INPUT[size_t(key_count)](); + memset(keyEventList, 0, sizeof(keyEventList)); + KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); + KeyboardManagerHelper::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); + + lock.unlock(); + UINT res = SendInput(key_count, keyEventList, sizeof(INPUT)); + delete[] keyEventList; + + // Reset the long press flag when the key has been lifted. + if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) + { + lock.lock(); + keyboardManagerState.singleKeyToggleToMod[data->lParam->vkCode] = false; + lock.unlock(); + } + + return 1; + } + } + + return 0; + } + + // Function to a handle a shortcut remap + intptr_t HandleShortcutRemapEvent(LowlevelKeyboardEvent* data, std::map& reMap, std::mutex& map_mutex) noexcept + { + // The mutex should be unlocked before SendInput is called to avoid re-entry into the same mutex. More details can be found at https://github.com/microsoft/PowerToys/pull/1789#issuecomment-607555837 + std::unique_lock lock(map_mutex); + for (auto& it : reMap) + { + const size_t src_size = it.first.Size(); + const size_t dest_size = it.second.targetShortcut.Size(); + + // If the shortcut has been pressed down + if (!it.second.isShortcutInvoked && it.first.CheckModifiersKeyboardState()) + { + if (data->lParam->vkCode == it.first.GetActionKey() && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)) + { + // Check if any other keys have been pressed apart from the shortcut. If true, then check for the next shortcut + if (!it.first.IsKeyboardStateClearExceptShortcut()) + { + continue; + } + + size_t key_count; + LPINPUT keyEventList; + + // Remember which win key was pressed initially + if (GetAsyncKeyState(VK_RWIN) & 0x8000) + { + it.second.winKeyInvoked = ModifierKey::Right; + } + else if (GetAsyncKeyState(VK_LWIN) & 0x8000) + { + it.second.winKeyInvoked = ModifierKey::Left; + } + + // Get the common keys between the two shortcuts + int commonKeys = it.first.GetCommonModifiersCount(it.second.targetShortcut); + + // If the original shortcut modifiers are a subset of the new shortcut + 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](); + memset(keyEventList, 0, sizeof(keyEventList)); + int i = 0; + + if ((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) && it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && it.second.targetShortcut.GetCtrlKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetCtrlKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) && it.second.targetShortcut.GetAltKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetAltKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) && it.second.targetShortcut.GetShiftKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetShiftKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + 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 = 1 + (src_size - 1) + (dest_size) - (2 * (size_t)commonKeys); + keyEventList = new INPUT[key_count](); + memset(keyEventList, 0, sizeof(keyEventList)); + + // Send dummy key + int i = 0; + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerConstants::DUMMY_KEY, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + // Release original shortcut state (release in reverse order of shortcut to be accurate) + if ((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) && it.first.GetShiftKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetShiftKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) && it.first.GetAltKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetAltKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && it.first.GetCtrlKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetCtrlKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) && it.first.GetWinKey(it.second.winKeyInvoked) != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetWinKey(it.second.winKeyInvoked), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + + // Set new shortcut key down state + if ((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) && it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && it.second.targetShortcut.GetCtrlKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetCtrlKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) && it.second.targetShortcut.GetAltKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetAltKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) && it.second.targetShortcut.GetShiftKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetShiftKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + + it.second.isShortcutInvoked = true; + lock.unlock(); + UINT res = SendInput((UINT)key_count, keyEventList, sizeof(INPUT)); + delete[] keyEventList; + return 1; + } + } + // The shortcut has already been pressed down at least once, i.e. the shortcut has been invoked + // There are 4 cases to be handled if the shortcut has been pressed down + // 1. The user lets go of one of the modifier keys - reset the keyboard back to the state of the keys actually being pressed down + // 2. The user keeps the shortcut pressed - the shortcut is repeated (for example you could hold down Ctrl+V and it will keep pasting) + // 3. The user lets go of the action key - reset the keyboard back to the state of the keys actually being pressed down + // 4. The user presses another key while holding the shortcut down - the system now sees all the new shortcut keys and this extra key pressed at the end. Not handled as resetting the state would trigger the original shortcut once more + else if (it.second.isShortcutInvoked) + { + // Get the common keys between the two shortcuts + int commonKeys = it.first.GetCommonModifiersCount(it.second.targetShortcut); + + // Case 1: If any of the modifier keys of the original shortcut are released before the normal key + 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; + // if the released key is present in both shortcuts' modifiers (i.e part of the common modifiers) + if (it.second.targetShortcut.CheckWinKey(data->lParam->vkCode) || it.second.targetShortcut.CheckCtrlKey(data->lParam->vkCode) || it.second.targetShortcut.CheckAltKey(data->lParam->vkCode) || it.second.targetShortcut.CheckShiftKey(data->lParam->vkCode)) + { + // release all new shortcut keys and the common released modifier except the other common modifiers, and add all original shortcut modifiers except the common ones + key_count = (dest_size - commonKeys + 1) + (src_size - 1 - commonKeys); + } + else + { + // release all new shortcut keys except the common modifiers and add all original shortcut modifiers except the common ones + key_count = dest_size + (src_size - 2) - (2 * (size_t)commonKeys); + } + LPINPUT keyEventList = new INPUT[key_count](); + memset(keyEventList, 0, sizeof(keyEventList)); + + // Release new shortcut state (release in reverse order of shortcut to be accurate) + int i = 0; + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + if (((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) || (it.second.targetShortcut.CheckShiftKey(data->lParam->vkCode))) && it.second.targetShortcut.GetShiftKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetShiftKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if (((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) || (it.second.targetShortcut.CheckAltKey(data->lParam->vkCode))) && it.second.targetShortcut.GetAltKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetAltKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if (((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) || (it.second.targetShortcut.CheckCtrlKey(data->lParam->vkCode))) && it.second.targetShortcut.GetCtrlKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetCtrlKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if (((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) || (it.second.targetShortcut.CheckWinKey(data->lParam->vkCode))) && it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + + // Set original shortcut key down state except the action key and the released modifier + if ((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) && (!it.first.CheckWinKey(data->lParam->vkCode)) && it.first.GetWinKey(it.second.winKeyInvoked) != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetWinKey(it.second.winKeyInvoked), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && (!it.first.CheckCtrlKey(data->lParam->vkCode)) && it.first.GetCtrlKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetCtrlKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) && (!it.first.CheckAltKey(data->lParam->vkCode)) && it.first.GetAltKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetAltKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) && (!it.first.CheckShiftKey(data->lParam->vkCode)) && it.first.GetShiftKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetShiftKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + + it.second.isShortcutInvoked = false; + it.second.winKeyInvoked = ModifierKey::Disabled; + lock.unlock(); + UINT res = SendInput((UINT)key_count, keyEventList, sizeof(INPUT)); + delete[] keyEventList; + return 1; + } + + // The system will see the modifiers of the new shortcut as being held down because of the shortcut remap + if (it.second.targetShortcut.CheckModifiersKeyboardState()) + { + // 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() && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)) + { + size_t key_count = 1; + LPINPUT keyEventList = new INPUT[key_count](); + memset(keyEventList, 0, sizeof(keyEventList)); + KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + + it.second.isShortcutInvoked = true; + lock.unlock(); + UINT res = SendInput((UINT)key_count, keyEventList, sizeof(INPUT)); + delete[] keyEventList; + return 1; + } + + // Case 3: If the action key is released from the original shortcut then revert the keyboard state to just the original modifiers being held down + if (data->lParam->vkCode == it.first.GetActionKey() && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)) + { + size_t key_count; + LPINPUT keyEventList; + + // If the original shortcut is a subset of the new shortcut + if (commonKeys == src_size - 1) + { + key_count = dest_size - commonKeys; + keyEventList = new INPUT[key_count](); + memset(keyEventList, 0, sizeof(keyEventList)); + + int i = 0; + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + if ((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) && it.second.targetShortcut.GetShiftKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetShiftKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) && it.second.targetShortcut.GetAltKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetAltKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && it.second.targetShortcut.GetCtrlKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetCtrlKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) && it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + } + else + { + // Key up for all new shortcut keys, key down for original shortcut modifiers and dummy key but common keys aren't repeated + key_count = (dest_size) + (src_size - 1) + 1 - (2 * (size_t)commonKeys); + keyEventList = new INPUT[key_count](); + memset(keyEventList, 0, sizeof(keyEventList)); + + // Release new shortcut state (release in reverse order of shortcut to be accurate) + int i = 0; + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + if ((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) && it.second.targetShortcut.GetShiftKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetShiftKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) && it.second.targetShortcut.GetAltKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetAltKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && it.second.targetShortcut.GetCtrlKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetCtrlKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) && it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + + // Set old shortcut key down state + + if ((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) && it.first.GetWinKey(it.second.winKeyInvoked) != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetWinKey(it.second.winKeyInvoked), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && it.first.GetCtrlKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetCtrlKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) && it.first.GetAltKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetAltKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + if ((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) && it.first.GetShiftKey() != NULL) + { + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetShiftKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + + // Send dummy key + KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerConstants::DUMMY_KEY, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + i++; + } + + it.second.isShortcutInvoked = false; + it.second.winKeyInvoked = ModifierKey::Disabled; + lock.unlock(); + UINT res = SendInput((UINT)key_count, keyEventList, sizeof(INPUT)); + delete[] keyEventList; + return 1; + } + } + } + } + + return 0; + } + + // Function to a handle an os-level shortcut remap + intptr_t HandleOSLevelShortcutRemapEvent(LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept + { + // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. + if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG) + { + bool result = HandleShortcutRemapEvent(data, keyboardManagerState.osLevelShortcutReMap, keyboardManagerState.osLevelShortcutReMap_mutex); + return result; + } + + return 0; + } + + // Function to a handle an app-specific shortcut remap + intptr_t HandleAppSpecificShortcutRemapEvent(LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept + { + // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. + if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG) + { + std::wstring process_name = KeyboardManagerHelper::GetCurrentApplication(false); + if (process_name.empty()) + { + return 0; + } + + std::unique_lock lock(keyboardManagerState.appSpecificShortcutReMap_mutex); + auto it = keyboardManagerState.appSpecificShortcutReMap.find(process_name); + if (it != keyboardManagerState.appSpecificShortcutReMap.end()) + { + lock.unlock(); + bool result = HandleShortcutRemapEvent(data, keyboardManagerState.appSpecificShortcutReMap[process_name], keyboardManagerState.appSpecificShortcutReMap_mutex); + return result; + } + } + + return 0; + } +} diff --git a/src/modules/keyboardmanager/dll/KeyboardEventHandlers.h b/src/modules/keyboardmanager/dll/KeyboardEventHandlers.h new file mode 100644 index 0000000000..9faea52cea --- /dev/null +++ b/src/modules/keyboardmanager/dll/KeyboardEventHandlers.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include + +namespace KeyboardEventHandlers +{ + // Function to a handle a single key remap + intptr_t HandleSingleKeyRemapEvent(LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept; + + // Function to a change a key's behavior from toggle to modifier + intptr_t HandleSingleKeyToggleToModEvent(LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept; + + // Function to a handle a shortcut remap + intptr_t HandleShortcutRemapEvent(LowlevelKeyboardEvent* data, std::map& reMap, std::mutex& map_mutex) noexcept; + + // Function to a handle an os-level shortcut remap + intptr_t HandleOSLevelShortcutRemapEvent(LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept; + + // Function to a handle an app-specific shortcut remap + intptr_t HandleAppSpecificShortcutRemapEvent(LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept; +}; diff --git a/src/modules/keyboardmanager/dll/KeyboardManager.vcxproj b/src/modules/keyboardmanager/dll/KeyboardManager.vcxproj index 8619670c41..2f825c253d 100644 --- a/src/modules/keyboardmanager/dll/KeyboardManager.vcxproj +++ b/src/modules/keyboardmanager/dll/KeyboardManager.vcxproj @@ -95,6 +95,10 @@ $(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories) + true + true + 4002 + 4002 shcore.lib;shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) @@ -110,11 +114,13 @@ + + Create diff --git a/src/modules/keyboardmanager/dll/KeyboardManager.vcxproj.filters b/src/modules/keyboardmanager/dll/KeyboardManager.vcxproj.filters new file mode 100644 index 0000000000..1d7b219fac --- /dev/null +++ b/src/modules/keyboardmanager/dll/KeyboardManager.vcxproj.filters @@ -0,0 +1,49 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + {5eaa2e5d-b1c1-4d0b-b703-4024a0e54e26} + cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx + + + {3b69ce82-e6e2-4e44-9ffc-a2e183403cd2} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {92a52637-acd2-441f-822d-430b726eade6} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/modules/keyboardmanager/dll/dllmain.cpp b/src/modules/keyboardmanager/dll/dllmain.cpp index 59c285cdb8..5953dbd7a9 100644 --- a/src/modules/keyboardmanager/dll/dllmain.cpp +++ b/src/modules/keyboardmanager/dll/dllmain.cpp @@ -13,6 +13,7 @@ #include #include #include +#include "KeyboardEventHandlers.h" extern "C" IMAGE_DOS_HEADER __ImageBase; @@ -43,13 +44,6 @@ private: // The PowerToy name that will be shown in the settings. const std::wstring app_name = GET_RESOURCE_STRING(IDS_KEYBOARDMANAGER); - // Flags used for distinguishing key events sent by Keyboard Manager - static const ULONG_PTR KEYBOARDMANAGER_SINGLEKEY_FLAG = 0x11; - static const ULONG_PTR KEYBOARDMANAGER_SHORTCUT_FLAG = 0x101; - - // Dummy key event used in between key up and down events to prevent certain global events from happening - static const DWORD DUMMY_KEY = 0xFF; - // Low level hook handles static HHOOK hook_handle; @@ -144,30 +138,6 @@ public: // This function is used to add the hardcoded mappings void init_map() { - //// If mapped to 0x0 then key is disabled. - //keyboardManagerState.singleKeyReMap[0x41] = 0x42; - //keyboardManagerState.singleKeyReMap[0x42] = 0x43; - //keyboardManagerState.singleKeyReMap[0x43] = 0x41; - //keyboardManagerState.singleKeyReMap[VK_LWIN] = VK_LCONTROL; - //keyboardManagerState.singleKeyReMap[VK_LCONTROL] = VK_RWIN; - //keyboardManagerState.singleKeyReMap[VK_CAPITAL] = 0x0; - //keyboardManagerState.singleKeyReMap[VK_LSHIFT] = VK_CAPITAL; - //keyboardManagerState.singleKeyToggleToMod[VK_CAPITAL] = false; - - //// OS-level shortcut remappings - //Shortcut newShortcut = Shortcut::CreateShortcut(winrt::to_hstring(L"Win 65")); - //Shortcut originalShortcut = Shortcut::CreateShortcut(winrt::to_hstring(L"Shift 65")); - //keyboardManagerState.AddOSLevelShortcut(originalShortcut, newShortcut); - //keyboardManagerState.osLevelShortcutReMap[std::vector({ VK_LMENU, 0x44 })] = std::make_pair(std::vector({ VK_LCONTROL, 0x56 }), false); - //keyboardManagerState.osLevelShortcutReMap[std::vector({ VK_LMENU, 0x45 })] = std::make_pair(std::vector({ VK_LCONTROL, 0x58 }), false); - //keyboardManagerState.osLevelShortcutReMap[std::vector({ VK_LWIN, 0x46 })] = std::make_pair(std::vector({ VK_LWIN, 0x53 }), false); - //keyboardManagerState.osLevelShortcutReMap[std::vector({ VK_LWIN, 0x41 })] = std::make_pair(std::vector({ VK_LCONTROL, 0x58 }), false); - //keyboardManagerState.osLevelShortcutReMap[std::vector({ VK_LCONTROL, 0x58 })] = std::make_pair(std::vector({ VK_LWIN, 0x41 }), false); - - //keyboardManagerState.osLevelShortcutReMap[std::vector({ VK_LWIN, 0x41 })] = std::make_pair(std::vector({ VK_LCONTROL, 0x58 }), false); - //keyboardManagerState.osLevelShortcutReMap[std::vector({ VK_LCONTROL, 0x58 })] = std::make_pair(std::vector({ VK_LMENU, 0x44 }), false); - //keyboardManagerState.osLevelShortcutReMap[std::vector({ VK_LCONTROL, 0x56 })] = std::make_pair(std::vector({ VK_LWIN, 0x41 }), false); - ////App-specific shortcut remappings //keyboardManagerState.appSpecificShortcutReMap[L"msedge.exe"][std::vector({ VK_LCONTROL, 0x43 })] = std::make_pair(std::vector({ VK_LCONTROL, 0x56 }), false); // Ctrl+C to Ctrl+V //keyboardManagerState.appSpecificShortcutReMap[L"msedge.exe"][std::vector({ VK_LMENU, 0x44 })] = std::make_pair(std::vector({ VK_LCONTROL, 0x46 }), false); // Alt+D to Ctrl+F @@ -366,7 +336,7 @@ public: } // Remap a key - intptr_t SingleKeyRemapResult = HandleSingleKeyRemapEvent(data); + intptr_t SingleKeyRemapResult = KeyboardEventHandlers::HandleSingleKeyRemapEvent(data, keyboardManagerState); // Single key remaps have priority. If a key is remapped, only the remapped version should be visible to the shortcuts and hence the event should be suppressed here. if (SingleKeyRemapResult == 1) @@ -386,10 +356,10 @@ public: } //// Remap a key to behave like a modifier instead of a toggle - //intptr_t SingleKeyToggleToModResult = HandleSingleKeyToggleToModEvent(data); + //intptr_t SingleKeyToggleToModResult = KeyboardEventHandlers::HandleSingleKeyToggleToModEvent(data, keyboardManagerState); //// Handle an app-specific shortcut remapping - //intptr_t AppSpecificShortcutRemapResult = HandleAppSpecificShortcutRemapEvent(data); + //intptr_t AppSpecificShortcutRemapResult = KeyboardEventHandlers::HandleAppSpecificShortcutRemapEvent(data, keyboardManagerState); //// If an app-specific shortcut is remapped then the os-level shortcut remapping should be suppressed. //if (AppSpecificShortcutRemapResult == 1) @@ -398,7 +368,7 @@ public: //} // Handle an os-level shortcut remapping - intptr_t OSLevelShortcutRemapResult = HandleOSLevelShortcutRemapEvent(data); + intptr_t OSLevelShortcutRemapResult = KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent(data, keyboardManagerState); // If any of the supported types of remappings took place, then suppress the key event if ((SingleKeyRemapResult + OSLevelShortcutRemapResult) > 0) @@ -410,547 +380,6 @@ public: return 0; } } - - void SetKeyEvent(LPINPUT keyEventArray, int index, DWORD inputType, WORD keyCode, DWORD flags, ULONG_PTR extraInfo) - { - keyEventArray[index].type = inputType; - keyEventArray[index].ki.wVk = keyCode; - keyEventArray[index].ki.dwFlags = flags; - if (KeyboardManagerHelper::IsExtendedKey(keyCode)) - { - keyEventArray[index].ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; - } - keyEventArray[index].ki.dwExtraInfo = extraInfo; - } - - // Function to a handle a single key remap - intptr_t HandleSingleKeyRemapEvent(LowlevelKeyboardEvent* data) noexcept - { - // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. - if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG)) - { - // The mutex should be unlocked before SendInput is called to avoid re-entry into the same mutex. More details can be found at https://github.com/microsoft/PowerToys/pull/1789#issuecomment-607555837 - std::unique_lock lock(keyboardManagerState.singleKeyReMap_mutex); - auto it = keyboardManagerState.singleKeyReMap.find(data->lParam->vkCode); - if (it != keyboardManagerState.singleKeyReMap.end()) - { - // If mapped to 0x0 then the key is disabled - if (it->second == 0x0) - { - return 1; - } - - int key_count = 1; - LPINPUT keyEventList = new INPUT[size_t(key_count)](); - memset(keyEventList, 0, sizeof(keyEventList)); - - // Handle remaps to VK_WIN_BOTH - DWORD target = it->second; - // If a key is remapped to VK_WIN_BOTH, we send VK_LWIN instead - if (target == CommonSharedConstants::VK_WIN_BOTH) - { - target = VK_LWIN; - } - - if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) - { - SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, KEYEVENTF_KEYUP, KEYBOARDMANAGER_SINGLEKEY_FLAG); - } - else - { - SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, 0, KEYBOARDMANAGER_SINGLEKEY_FLAG); - } - - lock.unlock(); - UINT res = SendInput(key_count, keyEventList, sizeof(INPUT)); - delete[] keyEventList; - return 1; - } - } - - return 0; - } - - // Function to a change a key's behavior from toggle to modifier - intptr_t HandleSingleKeyToggleToModEvent(LowlevelKeyboardEvent* data) noexcept - { - // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. - if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG)) - { - // The mutex should be unlocked before SendInput is called to avoid re-entry into the same mutex. More details can be found at https://github.com/microsoft/PowerToys/pull/1789#issuecomment-607555837 - std::unique_lock lock(keyboardManagerState.singleKeyToggleToMod_mutex); - auto it = keyboardManagerState.singleKeyToggleToMod.find(data->lParam->vkCode); - if (it != keyboardManagerState.singleKeyToggleToMod.end()) - { - // To avoid long presses (which leads to continuous keydown messages) from toggling the key on and off - if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN) - { - if (it->second == false) - { - keyboardManagerState.singleKeyToggleToMod[data->lParam->vkCode] = true; - } - else - { - lock.unlock(); - return 1; - } - } - int key_count = 2; - LPINPUT keyEventList = new INPUT[size_t(key_count)](); - memset(keyEventList, 0, sizeof(keyEventList)); - SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, KEYBOARDMANAGER_SINGLEKEY_FLAG); - SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, KEYEVENTF_KEYUP, KEYBOARDMANAGER_SINGLEKEY_FLAG); - - lock.unlock(); - UINT res = SendInput(key_count, keyEventList, sizeof(INPUT)); - delete[] keyEventList; - - // Reset the long press flag when the key has been lifted. - if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) - { - lock.lock(); - keyboardManagerState.singleKeyToggleToMod[data->lParam->vkCode] = false; - lock.unlock(); - } - - return 1; - } - } - - return 0; - } - - // Function to a handle a shortcut remap - intptr_t HandleShortcutRemapEvent(LowlevelKeyboardEvent* data, std::map& reMap, std::mutex& map_mutex) noexcept - { - // The mutex should be unlocked before SendInput is called to avoid re-entry into the same mutex. More details can be found at https://github.com/microsoft/PowerToys/pull/1789#issuecomment-607555837 - std::unique_lock lock(map_mutex); - for (auto& it : reMap) - { - const size_t src_size = it.first.Size(); - const size_t dest_size = it.second.targetShortcut.Size(); - - // If the shortcut has been pressed down - if (!it.second.isShortcutInvoked && it.first.CheckModifiersKeyboardState()) - { - if (data->lParam->vkCode == it.first.GetActionKey() && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)) - { - // Check if any other keys have been pressed apart from the shortcut. If true, then check for the next shortcut - if (!it.first.IsKeyboardStateClearExceptShortcut()) - { - continue; - } - - size_t key_count; - LPINPUT keyEventList; - - // Remember which win key was pressed initially - if (GetAsyncKeyState(VK_RWIN) & 0x8000) - { - it.second.winKeyInvoked = ModifierKey::Right; - } - else if (GetAsyncKeyState(VK_LWIN) & 0x8000) - { - it.second.winKeyInvoked = ModifierKey::Left; - } - - // Get the common keys between the two shortcuts - int commonKeys = it.first.GetCommonModifiersCount(it.second.targetShortcut); - - // If the original shortcut modifiers are a subset of the new shortcut - 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](); - memset(keyEventList, 0, sizeof(keyEventList)); - int i = 0; - - if ((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) && it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && it.second.targetShortcut.GetCtrlKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetCtrlKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) && it.second.targetShortcut.GetAltKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetAltKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) && it.second.targetShortcut.GetShiftKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetShiftKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetActionKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - 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 = 1 + (src_size - 1) + (dest_size) - (2 * (size_t)commonKeys); - keyEventList = new INPUT[key_count](); - memset(keyEventList, 0, sizeof(keyEventList)); - - // Send dummy key - int i = 0; - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)DUMMY_KEY, KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - // Release original shortcut state (release in reverse order of shortcut to be accurate) - if ((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) && it.first.GetShiftKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetShiftKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) && it.first.GetAltKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetAltKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && it.first.GetCtrlKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetCtrlKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) && it.first.GetWinKey(it.second.winKeyInvoked) != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetWinKey(it.second.winKeyInvoked), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - - // Set new shortcut key down state - if ((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) && it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && it.second.targetShortcut.GetCtrlKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetCtrlKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) && it.second.targetShortcut.GetAltKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetAltKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) && it.second.targetShortcut.GetShiftKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetShiftKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetActionKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - - it.second.isShortcutInvoked = true; - lock.unlock(); - UINT res = SendInput((UINT)key_count, keyEventList, sizeof(INPUT)); - delete[] keyEventList; - return 1; - } - } - // The shortcut has already been pressed down at least once, i.e. the shortcut has been invoked - // There are 4 cases to be handled if the shortcut has been pressed down - // 1. The user lets go of one of the modifier keys - reset the keyboard back to the state of the keys actually being pressed down - // 2. The user keeps the shortcut pressed - the shortcut is repeated (for example you could hold down Ctrl+V and it will keep pasting) - // 3. The user lets go of the action key - reset the keyboard back to the state of the keys actually being pressed down - // 4. The user presses another key while holding the shortcut down - the system now sees all the new shortcut keys and this extra key pressed at the end. Not handled as resetting the state would trigger the original shortcut once more - else if (it.second.isShortcutInvoked) - { - // Get the common keys between the two shortcuts - int commonKeys = it.first.GetCommonModifiersCount(it.second.targetShortcut); - - // Case 1: If any of the modifier keys of the original shortcut are released before the normal key - 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; - // if the released key is present in both shortcuts' modifiers (i.e part of the common modifiers) - if (it.second.targetShortcut.CheckWinKey(data->lParam->vkCode) || it.second.targetShortcut.CheckCtrlKey(data->lParam->vkCode) || it.second.targetShortcut.CheckAltKey(data->lParam->vkCode) || it.second.targetShortcut.CheckShiftKey(data->lParam->vkCode)) - { - // release all new shortcut keys and the common released modifier except the other common modifiers, and add all original shortcut modifiers except the common ones - key_count = (dest_size - commonKeys + 1) + (src_size - 1 - commonKeys); - } - else - { - // release all new shortcut keys except the common modifiers and add all original shortcut modifiers except the common ones - key_count = dest_size + (src_size - 2) - (2 * (size_t)commonKeys); - } - LPINPUT keyEventList = new INPUT[key_count](); - memset(keyEventList, 0, sizeof(keyEventList)); - - // Release new shortcut state (release in reverse order of shortcut to be accurate) - int i = 0; - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetActionKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - if (((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) || (it.second.targetShortcut.CheckShiftKey(data->lParam->vkCode))) && it.second.targetShortcut.GetShiftKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetShiftKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if (((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) || (it.second.targetShortcut.CheckAltKey(data->lParam->vkCode))) && it.second.targetShortcut.GetAltKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetAltKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if (((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) || (it.second.targetShortcut.CheckCtrlKey(data->lParam->vkCode))) && it.second.targetShortcut.GetCtrlKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetCtrlKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if (((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) || (it.second.targetShortcut.CheckWinKey(data->lParam->vkCode))) && it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - - // Set original shortcut key down state except the action key and the released modifier - if ((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) && (!it.first.CheckWinKey(data->lParam->vkCode)) && it.first.GetWinKey(it.second.winKeyInvoked) != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetWinKey(it.second.winKeyInvoked), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && (!it.first.CheckCtrlKey(data->lParam->vkCode)) && it.first.GetCtrlKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetCtrlKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) && (!it.first.CheckAltKey(data->lParam->vkCode)) && it.first.GetAltKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetAltKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) && (!it.first.CheckShiftKey(data->lParam->vkCode)) && it.first.GetShiftKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetShiftKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - - it.second.isShortcutInvoked = false; - it.second.winKeyInvoked = ModifierKey::Disabled; - lock.unlock(); - UINT res = SendInput((UINT)key_count, keyEventList, sizeof(INPUT)); - delete[] keyEventList; - return 1; - } - - // The system will see the modifiers of the new shortcut as being held down because of the shortcut remap - if (it.second.targetShortcut.CheckModifiersKeyboardState()) - { - // 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() && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)) - { - size_t key_count = 1; - LPINPUT keyEventList = new INPUT[key_count](); - memset(keyEventList, 0, sizeof(keyEventList)); - SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetActionKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - - it.second.isShortcutInvoked = true; - lock.unlock(); - UINT res = SendInput((UINT)key_count, keyEventList, sizeof(INPUT)); - delete[] keyEventList; - return 1; - } - - // Case 3: If the action key is released from the original shortcut then revert the keyboard state to just the original modifiers being held down - if (data->lParam->vkCode == it.first.GetActionKey() && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)) - { - size_t key_count; - LPINPUT keyEventList; - - // If the original shortcut is a subset of the new shortcut - if (commonKeys == src_size - 1) - { - key_count = dest_size - commonKeys; - keyEventList = new INPUT[key_count](); - memset(keyEventList, 0, sizeof(keyEventList)); - - int i = 0; - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetActionKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - if ((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) && it.second.targetShortcut.GetShiftKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetShiftKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) && it.second.targetShortcut.GetAltKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetAltKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && it.second.targetShortcut.GetCtrlKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetCtrlKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) && it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - } - else - { - // Key up for all new shortcut keys, key down for original shortcut modifiers and dummy key but common keys aren't repeated - key_count = (dest_size) + (src_size - 1) + 1 - (2 * (size_t)commonKeys); - keyEventList = new INPUT[key_count](); - memset(keyEventList, 0, sizeof(keyEventList)); - - // Release new shortcut state (release in reverse order of shortcut to be accurate) - int i = 0; - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetActionKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - if ((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) && it.second.targetShortcut.GetShiftKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetShiftKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) && it.second.targetShortcut.GetAltKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetAltKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && it.second.targetShortcut.GetCtrlKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetCtrlKey(), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) && it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked), KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - - // Set old shortcut key down state - - if ((it.second.targetShortcut.GetWinKey(it.second.winKeyInvoked) != it.first.GetWinKey(it.second.winKeyInvoked)) && it.first.GetWinKey(it.second.winKeyInvoked) != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetWinKey(it.second.winKeyInvoked), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && it.first.GetCtrlKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetCtrlKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetAltKey() != it.first.GetAltKey()) && it.first.GetAltKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetAltKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - if ((it.second.targetShortcut.GetShiftKey() != it.first.GetShiftKey()) && it.first.GetShiftKey() != NULL) - { - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetShiftKey(), 0, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - - // Send dummy key - SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)DUMMY_KEY, KEYEVENTF_KEYUP, KEYBOARDMANAGER_SHORTCUT_FLAG); - i++; - } - - it.second.isShortcutInvoked = false; - it.second.winKeyInvoked = ModifierKey::Disabled; - lock.unlock(); - UINT res = SendInput((UINT)key_count, keyEventList, sizeof(INPUT)); - delete[] keyEventList; - return 1; - } - } - } - } - - return 0; - } - - // Function to a handle an os-level shortcut remap - intptr_t HandleOSLevelShortcutRemapEvent(LowlevelKeyboardEvent* data) noexcept - { - // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. - if (data->lParam->dwExtraInfo != KEYBOARDMANAGER_SHORTCUT_FLAG) - { - bool result = HandleShortcutRemapEvent(data, keyboardManagerState.osLevelShortcutReMap, keyboardManagerState.osLevelShortcutReMap_mutex); - return result; - } - - return 0; - } - - // Function to return the window in focus - HWND GetFocusWindowHandle() - { - // Using GetGUIThreadInfo for getting the process of the window in focus. GetForegroundWindow has issues with UWP apps as it returns the Application Frame Host as its linked process - GUITHREADINFO guiThreadInfo; - guiThreadInfo.cbSize = sizeof(GUITHREADINFO); - GetGUIThreadInfo(0, &guiThreadInfo); - - // If no window in focus, use the active window - if (guiThreadInfo.hwndFocus == nullptr) - { - return guiThreadInfo.hwndActive; - } - return guiThreadInfo.hwndFocus; - } - - // Function to return the executable name of the application in focus - std::wstring GetCurrentApplication(bool keepPath) - { - HWND current_window_handle = GetFocusWindowHandle(); - DWORD process_id; - DWORD nSize = MAX_PATH; - WCHAR buffer[MAX_PATH] = { 0 }; - - // Get process ID of the focus window - DWORD thread_id = GetWindowThreadProcessId(current_window_handle, &process_id); - HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_id); - - // Get full path of the executable - bool res = QueryFullProcessImageName(hProc, 0, buffer, &nSize); - std::wstring process_name; - CloseHandle(hProc); - - process_name = buffer; - if (res) - { - PathStripPath(buffer); - - if (!keepPath) - { - process_name = buffer; - } - } - return process_name; - } - - // Function to a handle an app-specific shortcut remap - intptr_t HandleAppSpecificShortcutRemapEvent(LowlevelKeyboardEvent* data) noexcept - { - // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. - if (data->lParam->dwExtraInfo != KEYBOARDMANAGER_SHORTCUT_FLAG) - { - std::wstring process_name = GetCurrentApplication(false); - if (process_name.empty()) - { - return 0; - } - - std::unique_lock lock(keyboardManagerState.appSpecificShortcutReMap_mutex); - auto it = keyboardManagerState.appSpecificShortcutReMap.find(process_name); - if (it != keyboardManagerState.appSpecificShortcutReMap.end()) - { - lock.unlock(); - bool result = HandleShortcutRemapEvent(data, keyboardManagerState.appSpecificShortcutReMap[process_name], keyboardManagerState.appSpecificShortcutReMap_mutex); - return result; - } - } - - return 0; - } }; HHOOK KeyboardManager::hook_handle = nullptr; diff --git a/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj b/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj index 49eed47bd6..c33ad35cc9 100644 --- a/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj +++ b/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj @@ -68,6 +68,8 @@ stdcpp17 $(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories) MultiThreadedDebug + true + 4002 true @@ -86,6 +88,8 @@ true $(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories) MultiThreaded + true + 4002 true diff --git a/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj.filters b/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj.filters index ffb4e69407..86da96c16c 100644 --- a/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj.filters +++ b/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj.filters @@ -1,28 +1,78 @@  - - - - - - - - - + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + - - - - - - - - - + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + {7ccc5562-a9e1-4a3a-9f23-bdfee9ed5776} + cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx + + + {80d1fd84-2f25-463b-9fc7-ab7e7e9529c0} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {7bd580d1-f340-4817-9893-e5cbfd20cf54} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + \ No newline at end of file