mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 19:57:57 +01:00
[Keyboard Manager] Added in Shortcut to Key and Key to Shortcut remapping (#5070)
* Added union class * Added key to shortcut backend implementation * Added tests * Added tests for CapsLock/modifier workaround for key to shortcut * Added correct JSON loading step * Cleaned shortcut remap code to use helper function for modifier keys * Removed RemapKey class * Enable Key to Shortcut in UI along with Type Shortcut in Remap key window * Fixed orphaning and unsuccessful remap dialog * Fixed column width * Renamed second type key button * Fixed Type Shortcut issues * Fixed shortcut to key backend logic and manually tested most scenarios * Added s2k in UI, manually verified its working * Added one more k2s test * Added tests for s2k * Added tests for Caps Lock workaround in shortcut remaps * Fixed formatting * Fixed formatting * Removed safety code since it can cause issues with code generated key up events * Added test for key up scenario * Tweaked warning text * Tweaked text * Tweaked text to fit in two lines * telemetry additions
This commit is contained in:
@@ -231,7 +231,7 @@
|
||||
<CustomControls:GroupTitleTextBlock x:Uid="KeyboardManager_RemapKeyboardHeader"
|
||||
IsActive="{x:Bind Path=ViewModel.Enabled, Mode=OneWay}"/>
|
||||
|
||||
<CustomControls:BodyTextBlock Text="Click below to remap a single key to another key"
|
||||
<CustomControls:BodyTextBlock Text="Click below to remap keys to other keys or shortcuts."
|
||||
IsActive="{x:Bind Path=ViewModel.Enabled, Mode=OneWay}"
|
||||
Margin="{StaticResource SmallTopMargin}"/>
|
||||
|
||||
@@ -270,7 +270,7 @@
|
||||
<CustomControls:GroupTitleTextBlock x:Uid="KeyboardManager_RemapShortcutsHeader"
|
||||
IsActive="{x:Bind Path=ViewModel.Enabled, Mode=OneWay}"/>
|
||||
|
||||
<CustomControls:BodyTextBlock Text="Click below to remap a shortcut (hotkey) to another shortcut"
|
||||
<CustomControls:BodyTextBlock Text="Click below to remap a shortcuts to other shortcuts or keys. Additionally, mappings can be targeted to specific applications as well."
|
||||
IsActive="{x:Bind Path=ViewModel.Enabled, Mode=OneWay}"/>
|
||||
|
||||
<Button x:Uid="KeyboardManager_RemapShortcutsButton"
|
||||
|
||||
@@ -138,13 +138,13 @@ namespace KeyboardManagerHelper
|
||||
case ErrorType::NoError:
|
||||
return L"Remapping successful";
|
||||
case ErrorType::SameKeyPreviouslyMapped:
|
||||
return L"Cannot remap a key more than once";
|
||||
return L"Cannot remap a key more than once for the same target app";
|
||||
case ErrorType::MapToSameKey:
|
||||
return L"Cannot remap a key to itself";
|
||||
case ErrorType::ConflictingModifierKey:
|
||||
return L"Cannot remap this key as it conflicts with another remapped key";
|
||||
case ErrorType::SameShortcutPreviouslyMapped:
|
||||
return L"Cannot remap a shortcut more than once";
|
||||
return L"Cannot remap a shortcut more than once for the same target app";
|
||||
case ErrorType::MapToSameShortcut:
|
||||
return L"Cannot remap a shortcut to itself";
|
||||
case ErrorType::ConflictingModifierShortcut:
|
||||
@@ -248,4 +248,81 @@ namespace KeyboardManagerHelper
|
||||
|
||||
return process_name;
|
||||
}
|
||||
|
||||
// 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 modfifier 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)
|
||||
{
|
||||
// If key down is to be sent, send in the order Win, Ctrl, Alt, Shift
|
||||
if (isKeyDown)
|
||||
{
|
||||
// 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 modfifier 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)))
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetWinKey(winKeyInvoked), 0, extraInfoFlag);
|
||||
index++;
|
||||
}
|
||||
if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckCtrlKey(keyToBeReleased)))
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetCtrlKey(), 0, extraInfoFlag);
|
||||
index++;
|
||||
}
|
||||
if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckAltKey(keyToBeReleased)))
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetAltKey(), 0, extraInfoFlag);
|
||||
index++;
|
||||
}
|
||||
if (shortcutToBeSent.GetShiftKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckShiftKey(keyToBeReleased)))
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetShiftKey(), 0, extraInfoFlag);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
// If key up is to be sent, send in the order Shift, Alt, Ctrl, Win
|
||||
else
|
||||
{
|
||||
// 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 modfifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
|
||||
if (shortcutToBeSent.GetShiftKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey() || shortcutToBeSent.CheckShiftKey(keyToBeReleased)))
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetShiftKey(), KEYEVENTF_KEYUP, extraInfoFlag);
|
||||
index++;
|
||||
}
|
||||
if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey() || shortcutToBeSent.CheckAltKey(keyToBeReleased)))
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetAltKey(), KEYEVENTF_KEYUP, extraInfoFlag);
|
||||
index++;
|
||||
}
|
||||
if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey() || shortcutToBeSent.CheckCtrlKey(keyToBeReleased)))
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetCtrlKey(), KEYEVENTF_KEYUP, extraInfoFlag);
|
||||
index++;
|
||||
}
|
||||
if (shortcutToBeSent.GetWinKey(winKeyInvoked) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(winKeyInvoked) != shortcutToCompare.GetWinKey(winKeyInvoked) || shortcutToBeSent.CheckWinKey(keyToBeReleased)))
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetWinKey(winKeyInvoked), KEYEVENTF_KEYUP, extraInfoFlag);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to filter the key codes for artificial key codes
|
||||
DWORD FilterArtificialKeys(const DWORD& key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
// If a key is remapped to VK_WIN_BOTH, we send VK_LWIN instead
|
||||
case CommonSharedConstants::VK_WIN_BOTH:
|
||||
return VK_LWIN;
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
// Function to sort a vector of shortcuts based on it's size
|
||||
void SortShortcutVectorBasedOnSize(std::vector<Shortcut>& shortcutVector)
|
||||
{
|
||||
std::sort(shortcutVector.begin(), shortcutVector.end(), [](Shortcut first, Shortcut second) {
|
||||
return first.Size() > second.Size();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
#include "Shortcut.h"
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
struct hstring;
|
||||
@@ -88,4 +90,13 @@ namespace KeyboardManagerHelper
|
||||
|
||||
// Function to return the executable name of the application in focus
|
||||
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 modfifier 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);
|
||||
|
||||
// Function to filter the key codes for artificial key codes
|
||||
DWORD FilterArtificialKeys(const DWORD& key);
|
||||
|
||||
// Function to sort a vector of shortcuts based on it's size
|
||||
void SortShortcutVectorBasedOnSize(std::vector<Shortcut>& shortcutVector);
|
||||
}
|
||||
@@ -121,6 +121,7 @@
|
||||
<ClCompile Include="trace.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ModifierKey.h" />
|
||||
<ClInclude Include="InputInterface.h" />
|
||||
<ClInclude Include="Helpers.h" />
|
||||
<ClInclude Include="KeyboardManagerConstants.h" />
|
||||
|
||||
@@ -65,6 +65,9 @@
|
||||
<ClInclude Include="InputInterface.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ModifierKey.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "pch.h"
|
||||
#include "KeyboardManagerState.h"
|
||||
#include "Shortcut.h"
|
||||
#include "RemapShortcut.h"
|
||||
#include <../common/settings_helpers.h>
|
||||
#include "KeyDelay.h"
|
||||
#include "Helpers.h"
|
||||
|
||||
// Constructor
|
||||
KeyboardManagerState::KeyboardManagerState() :
|
||||
@@ -103,6 +106,7 @@ void KeyboardManagerState::ClearOSLevelShortcuts()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(osLevelShortcutReMap_mutex);
|
||||
osLevelShortcutReMap.clear();
|
||||
osLevelShortcutReMapSortedKeys.clear();
|
||||
}
|
||||
|
||||
// Function to clear the Keys remapping table.
|
||||
@@ -117,10 +121,11 @@ void KeyboardManagerState::ClearAppSpecificShortcuts()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(appSpecificShortcutReMap_mutex);
|
||||
appSpecificShortcutReMap.clear();
|
||||
appSpecificShortcutReMapSortedKeys.clear();
|
||||
}
|
||||
|
||||
// Function to add a new OS level shortcut remapping
|
||||
bool KeyboardManagerState::AddOSLevelShortcut(const Shortcut& originalSC, const Shortcut& newSC)
|
||||
bool KeyboardManagerState::AddOSLevelShortcut(const Shortcut& originalSC, const std::variant<DWORD, Shortcut>& newSC)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(osLevelShortcutReMap_mutex);
|
||||
|
||||
@@ -132,11 +137,14 @@ bool KeyboardManagerState::AddOSLevelShortcut(const Shortcut& originalSC, const
|
||||
}
|
||||
|
||||
osLevelShortcutReMap[originalSC] = RemapShortcut(newSC);
|
||||
osLevelShortcutReMapSortedKeys.push_back(originalSC);
|
||||
KeyboardManagerHelper::SortShortcutVectorBasedOnSize(osLevelShortcutReMapSortedKeys);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function to add a new OS level shortcut remapping
|
||||
bool KeyboardManagerState::AddSingleKeyRemap(const DWORD& originalKey, const DWORD& newRemapKey)
|
||||
// Function to add a new single key to key/shortcut remapping
|
||||
bool KeyboardManagerState::AddSingleKeyRemap(const DWORD& originalKey, const std::variant<DWORD, Shortcut>& newRemapKey)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(singleKeyReMap_mutex);
|
||||
|
||||
@@ -152,28 +160,34 @@ bool KeyboardManagerState::AddSingleKeyRemap(const DWORD& originalKey, const DWO
|
||||
}
|
||||
|
||||
// Function to add a new App specific shortcut remapping
|
||||
bool KeyboardManagerState::AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const Shortcut& newSC)
|
||||
bool KeyboardManagerState::AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const std::variant<DWORD, Shortcut>& newSC)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(appSpecificShortcutReMap_mutex);
|
||||
|
||||
// Check if there are any app specific shortcuts for this app
|
||||
auto appIt = appSpecificShortcutReMap.find(app);
|
||||
if (appIt != appSpecificShortcutReMap.end())
|
||||
{
|
||||
// Check if the shortcut is already remapped
|
||||
auto shortcutIt = appSpecificShortcutReMap[app].find(originalSC);
|
||||
if (shortcutIt != appSpecificShortcutReMap[app].end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert app name to lower case
|
||||
std::wstring process_name;
|
||||
process_name.resize(app.length());
|
||||
std::transform(app.begin(), app.end(), process_name.begin(), towlower);
|
||||
|
||||
// Check if there are any app specific shortcuts for this app
|
||||
auto appIt = appSpecificShortcutReMap.find(process_name);
|
||||
if (appIt != appSpecificShortcutReMap.end())
|
||||
{
|
||||
// Check if the shortcut is already remapped
|
||||
auto shortcutIt = appSpecificShortcutReMap[process_name].find(originalSC);
|
||||
if (shortcutIt != appSpecificShortcutReMap[process_name].end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
appSpecificShortcutReMapSortedKeys[process_name] = std::vector<Shortcut>();
|
||||
}
|
||||
|
||||
appSpecificShortcutReMap[process_name][originalSC] = RemapShortcut(newSC);
|
||||
appSpecificShortcutReMapSortedKeys[process_name].push_back(originalSC);
|
||||
KeyboardManagerHelper::SortShortcutVectorBasedOnSize(appSpecificShortcutReMapSortedKeys[process_name]);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -349,10 +363,10 @@ KeyboardManagerHelper::KeyboardHookDecision KeyboardManagerState::DetectSingleRe
|
||||
}
|
||||
|
||||
// Function which can be used in HandleKeyboardHookEvent before the os level shortcut remap event to use the UI and suppress events while the remap window is active.
|
||||
KeyboardManagerHelper::KeyboardHookDecision KeyboardManagerState::DetectShortcutUIBackend(LowlevelKeyboardEvent* data)
|
||||
KeyboardManagerHelper::KeyboardHookDecision KeyboardManagerState::DetectShortcutUIBackend(LowlevelKeyboardEvent* data, bool isRemapKey)
|
||||
{
|
||||
// Check if the detect shortcut UI window has been activated
|
||||
if (CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated))
|
||||
if ((!isRemapKey && CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated)) || (isRemapKey && CheckUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated)))
|
||||
{
|
||||
if (HandleKeyDelayEvent(data))
|
||||
{
|
||||
@@ -375,7 +389,7 @@ KeyboardManagerHelper::KeyboardHookDecision KeyboardManagerState::DetectShortcut
|
||||
}
|
||||
|
||||
// If the detect shortcut UI window is not activated, then clear the shortcut buffer if it isn't empty
|
||||
else
|
||||
else if (!CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated) && !CheckUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated))
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(detectedShortcut_mutex);
|
||||
if (!detectedShortcut.IsEmpty())
|
||||
@@ -385,7 +399,7 @@ KeyboardManagerHelper::KeyboardHookDecision KeyboardManagerState::DetectShortcut
|
||||
}
|
||||
|
||||
// If the settings window is up, shortcut remappings should not be applied, but we should not suppress events in the hook
|
||||
if (CheckUIState(KeyboardManagerUIState::EditShortcutsWindowActivated))
|
||||
if (!isRemapKey && (CheckUIState(KeyboardManagerUIState::EditShortcutsWindowActivated)) || (isRemapKey && uiState == KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated))
|
||||
{
|
||||
return KeyboardManagerHelper::KeyboardHookDecision::SkipHook;
|
||||
}
|
||||
@@ -452,7 +466,18 @@ bool KeyboardManagerState::SaveConfigToFile()
|
||||
{
|
||||
json::JsonObject keys;
|
||||
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(winrt::to_hstring((unsigned int)it.first)));
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)it.second)));
|
||||
|
||||
// For key to key remapping
|
||||
if (it.second.index() == 0)
|
||||
{
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)std::get<DWORD>(it.second))));
|
||||
}
|
||||
|
||||
// For key to shortcut remapping
|
||||
else
|
||||
{
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(std::get<Shortcut>(it.second).ToHstringVK()));
|
||||
}
|
||||
|
||||
inProcessRemapKeysArray.Append(keys);
|
||||
}
|
||||
@@ -463,7 +488,18 @@ bool KeyboardManagerState::SaveConfigToFile()
|
||||
{
|
||||
json::JsonObject keys;
|
||||
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(it.first.ToHstringVK()));
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(it.second.targetShortcut.ToHstringVK()));
|
||||
|
||||
// For shortcut to key remapping
|
||||
if (it.second.targetShortcut.index() == 0)
|
||||
{
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)std::get<DWORD>(it.second.targetShortcut))));
|
||||
}
|
||||
|
||||
// For shortcut to shortcut remapping
|
||||
else
|
||||
{
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(std::get<Shortcut>(it.second.targetShortcut).ToHstringVK()));
|
||||
}
|
||||
|
||||
globalRemapShortcutsArray.Append(keys);
|
||||
}
|
||||
@@ -477,12 +513,23 @@ bool KeyboardManagerState::SaveConfigToFile()
|
||||
{
|
||||
json::JsonObject keys;
|
||||
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(itKeys.first.ToHstringVK()));
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(itKeys.second.targetShortcut.ToHstringVK()));
|
||||
|
||||
// For shortcut to key remapping
|
||||
if (itKeys.second.targetShortcut.index() == 0)
|
||||
{
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)std::get<DWORD>(itKeys.second.targetShortcut))));
|
||||
}
|
||||
|
||||
// For shortcut to shortcut remapping
|
||||
else
|
||||
{
|
||||
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(std::get<Shortcut>(itKeys.second.targetShortcut).ToHstringVK()));
|
||||
}
|
||||
|
||||
keys.SetNamedValue(KeyboardManagerConstants::TargetAppSettingName, json::value(itApp.first));
|
||||
|
||||
appSpecificRemapShortcutsArray.Append(keys);
|
||||
}
|
||||
|
||||
}
|
||||
lockAppSpecificShortcutReMap.unlock();
|
||||
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
#pragma once
|
||||
#include "Helpers.h"
|
||||
#include "Shortcut.h"
|
||||
#include "RemapShortcut.h"
|
||||
#include <mutex>
|
||||
#include "KeyboardManagerConstants.h"
|
||||
#include "../common/keyboard_layout.h"
|
||||
#include <functional>
|
||||
#include <interface/lowlevel_keyboard_event_data.h>
|
||||
#include <variant>
|
||||
#include "Shortcut.h"
|
||||
#include "RemapShortcut.h"
|
||||
|
||||
class KeyDelay;
|
||||
|
||||
namespace KeyboardManagerHelper
|
||||
{
|
||||
enum class KeyboardHookDecision;
|
||||
}
|
||||
|
||||
namespace winrt::Windows::UI::Xaml::Controls
|
||||
{
|
||||
struct StackPanel;
|
||||
@@ -22,6 +27,8 @@ enum class KeyboardManagerUIState
|
||||
Deactivated,
|
||||
// If set to this value then the detect key window is currently active and it requires a hook
|
||||
DetectSingleKeyRemapWindowActivated,
|
||||
// If set to this value then the detect shortcut window in edit keyboard window is currently active and it requires a hook
|
||||
DetectShortcutWindowInEditKeyboardWindowActivated,
|
||||
// If set to this value then the edit keyboard window is currently active and remaps should not be applied
|
||||
EditKeyboardWindowActivated,
|
||||
// If set to this value then the detect shortcut window is currently active and it requires a hook
|
||||
@@ -84,7 +91,7 @@ public:
|
||||
// The map members and their mutexes are left as public since the maps are used extensively in dllmain.cpp.
|
||||
// Maps which store the remappings for each of the features. The bool fields should be initialized to false. They are used to check the current state of the shortcut (i.e is that particular shortcut currently pressed down or not).
|
||||
// Stores single key remappings
|
||||
std::unordered_map<DWORD, DWORD> singleKeyReMap;
|
||||
std::unordered_map<DWORD, std::variant<DWORD, Shortcut>> singleKeyReMap;
|
||||
std::mutex singleKeyReMap_mutex;
|
||||
|
||||
// Stores keys which need to be changed from toggle behavior to modifier behavior. Eg. Caps Lock
|
||||
@@ -93,10 +100,12 @@ public:
|
||||
|
||||
// Stores the os level shortcut remappings
|
||||
std::map<Shortcut, RemapShortcut> osLevelShortcutReMap;
|
||||
std::vector<Shortcut> osLevelShortcutReMapSortedKeys;
|
||||
std::mutex osLevelShortcutReMap_mutex;
|
||||
|
||||
// Stores the app-specific shortcut remappings. Maps application name to the shortcut map
|
||||
std::map<std::wstring, std::map<Shortcut, RemapShortcut>> appSpecificShortcutReMap;
|
||||
std::map<std::wstring, std::vector<Shortcut>> appSpecificShortcutReMapSortedKeys;
|
||||
std::mutex appSpecificShortcutReMap_mutex;
|
||||
|
||||
// Stores the keyboard layout
|
||||
@@ -129,14 +138,14 @@ public:
|
||||
// Function to clear the App specific shortcut remapping table
|
||||
void ClearAppSpecificShortcuts();
|
||||
|
||||
// Function to add a new single key remapping
|
||||
bool AddSingleKeyRemap(const DWORD& originalKey, const DWORD& newRemapKey);
|
||||
// Function to add a new single key to key remapping
|
||||
bool AddSingleKeyRemap(const DWORD& originalKey, const std::variant<DWORD, Shortcut>& newRemapKey);
|
||||
|
||||
// Function to add a new OS level shortcut remapping
|
||||
bool AddOSLevelShortcut(const Shortcut& originalSC, const Shortcut& newSC);
|
||||
bool AddOSLevelShortcut(const Shortcut& originalSC, const std::variant<DWORD, Shortcut>& newSC);
|
||||
|
||||
// Function to add a new App specific level shortcut remapping
|
||||
bool AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const Shortcut& newSC);
|
||||
bool AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const std::variant<DWORD, Shortcut>& newSC);
|
||||
|
||||
// Function to set the textblock of the detect shortcut UI so that it can be accessed by the hook
|
||||
void ConfigureDetectShortcutUI(const winrt::Windows::UI::Xaml::Controls::StackPanel& textBlock1, const winrt::Windows::UI::Xaml::Controls::StackPanel& textBlock2);
|
||||
@@ -160,7 +169,7 @@ public:
|
||||
KeyboardManagerHelper::KeyboardHookDecision DetectSingleRemapKeyUIBackend(LowlevelKeyboardEvent* data);
|
||||
|
||||
// Function which can be used in HandleKeyboardHookEvent before the os level shortcut remap event to use the UI and suppress events while the remap window is active.
|
||||
KeyboardManagerHelper::KeyboardHookDecision DetectShortcutUIBackend(LowlevelKeyboardEvent* data);
|
||||
KeyboardManagerHelper::KeyboardHookDecision DetectShortcutUIBackend(LowlevelKeyboardEvent* data, bool isRemapKey);
|
||||
|
||||
// Add a KeyDelay object to get delayed key presses events for a given virtual key
|
||||
// NOTE: this will throw an exception if a virtual key is registered twice.
|
||||
|
||||
10
src/modules/keyboardmanager/common/ModifierKey.h
Normal file
10
src/modules/keyboardmanager/common/ModifierKey.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
// Enum type to store different states of the win key
|
||||
enum class ModifierKey
|
||||
{
|
||||
Disabled,
|
||||
Left,
|
||||
Right,
|
||||
Both
|
||||
};
|
||||
@@ -1,21 +1,22 @@
|
||||
#pragma once
|
||||
#include "Shortcut.h"
|
||||
#include <variant>
|
||||
|
||||
// This class stores all the variables associated with each shortcut remapping
|
||||
class RemapShortcut
|
||||
{
|
||||
public:
|
||||
Shortcut targetShortcut;
|
||||
std::variant<DWORD, Shortcut> targetShortcut;
|
||||
bool isShortcutInvoked;
|
||||
ModifierKey winKeyInvoked;
|
||||
|
||||
RemapShortcut(const Shortcut& sc) :
|
||||
RemapShortcut(const std::variant<DWORD, Shortcut>& sc) :
|
||||
targetShortcut(sc), isShortcutInvoked(false), winKeyInvoked(ModifierKey::Disabled)
|
||||
{
|
||||
}
|
||||
|
||||
RemapShortcut() :
|
||||
isShortcutInvoked(false), winKeyInvoked(ModifierKey::Disabled)
|
||||
targetShortcut(Shortcut()), isShortcutInvoked(false), winKeyInvoked(ModifierKey::Disabled)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "ModifierKey.h"
|
||||
class InputInterface;
|
||||
class LayoutMap;
|
||||
namespace KeyboardManagerHelper
|
||||
@@ -7,15 +7,6 @@ namespace KeyboardManagerHelper
|
||||
enum class ErrorType;
|
||||
}
|
||||
|
||||
// Enum type to store different states of the win key
|
||||
enum class ModifierKey
|
||||
{
|
||||
Disabled,
|
||||
Left,
|
||||
Right,
|
||||
Both
|
||||
};
|
||||
|
||||
class Shortcut
|
||||
{
|
||||
private:
|
||||
|
||||
@@ -30,34 +30,40 @@ void Trace::EnableKeyboardManager(const bool enabled) noexcept
|
||||
}
|
||||
|
||||
// Log number of key remaps when the user uses Edit Keyboard and saves settings
|
||||
void Trace::KeyRemapCount(const DWORD count) noexcept
|
||||
void Trace::KeyRemapCount(const DWORD keyToKeyCount, const DWORD keyToShortcutCount) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"KeyboardManager_KeyRemapCount",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(count, "KeyRemapCount"));
|
||||
TraceLoggingValue(keyToKeyCount + keyToShortcutCount, "KeyRemapCount"),
|
||||
TraceLoggingValue(keyToKeyCount, "KeyToKeyRemapCount"),
|
||||
TraceLoggingValue(keyToShortcutCount, "KeyToShortcutRemapCount"));
|
||||
}
|
||||
|
||||
// Log number of os level shortcut remaps when the user uses Edit Shortcuts and saves settings
|
||||
void Trace::OSLevelShortcutRemapCount(const DWORD count) noexcept
|
||||
void Trace::OSLevelShortcutRemapCount(const DWORD shortcutToShortcutCount, const DWORD shortcutToKeyCount) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"KeyboardManager_OSLevelShortcutRemapCount",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(count, "OSLevelShortcutRemapCount"));
|
||||
TraceLoggingValue(shortcutToShortcutCount + shortcutToKeyCount, "OSLevelShortcutRemapCount"),
|
||||
TraceLoggingValue(shortcutToShortcutCount, "OSLevelShortcutToShortcutRemapCount"),
|
||||
TraceLoggingValue(shortcutToKeyCount, "OSLevelShortcutToKeyRemapCount"));
|
||||
}
|
||||
|
||||
// Log number of app specific shortcut remaps when the user uses Edit Shortcuts and saves settings
|
||||
void Trace::AppSpecificShortcutRemapCount(const DWORD count) noexcept
|
||||
void Trace::AppSpecificShortcutRemapCount(const DWORD shortcutToShortcutCount, const DWORD shortcutToKeyCount) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"KeyboardManager_AppSpecificShortcutRemapCount",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(count, "AppSpecificShortcutRemapCount"));
|
||||
TraceLoggingValue(shortcutToShortcutCount + shortcutToKeyCount, "AppSpecificShortcutRemapCount"),
|
||||
TraceLoggingValue(shortcutToShortcutCount, "AppSpecificShortcutToShortcutRemapCount"),
|
||||
TraceLoggingValue(shortcutToKeyCount, "AppSpecificShortcutToKeyRemapCount"));
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ public:
|
||||
static void EnableKeyboardManager(const bool enabled) noexcept;
|
||||
|
||||
// Log number of key remaps when the user uses Edit Keyboard and saves settings
|
||||
static void KeyRemapCount(const DWORD count) noexcept;
|
||||
static void KeyRemapCount(const DWORD keyToKeyCount, const DWORD keyToShortcutCount) noexcept;
|
||||
|
||||
// Log number of os level shortcut remaps when the user uses Edit Shortcuts and saves settings
|
||||
static void OSLevelShortcutRemapCount(const DWORD count) noexcept;
|
||||
static void OSLevelShortcutRemapCount(const DWORD shortcutToShortcutCount, const DWORD shortcutToKeyCount) noexcept;
|
||||
|
||||
// Log number of app specific shortcut remaps when the user uses Edit Shortcuts and saves settings
|
||||
static void AppSpecificShortcutRemapCount(const DWORD count) noexcept;
|
||||
static void AppSpecificShortcutRemapCount(const DWORD shortcutToShortcutCount, const DWORD shortcutToKeyCount) noexcept;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#include "pch.h"
|
||||
#include "KeyboardEventHandlers.h"
|
||||
#include "keyboardmanager/common/Shortcut.h"
|
||||
#include "keyboardmanager/common/RemapShortcut.h"
|
||||
#include "../common/shared_constants.h"
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/common/InputInterface.h>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
|
||||
namespace KeyboardEventHandlers
|
||||
{
|
||||
@@ -17,30 +20,49 @@ namespace KeyboardEventHandlers
|
||||
auto it = keyboardManagerState.singleKeyReMap.find(data->lParam->vkCode);
|
||||
if (it != keyboardManagerState.singleKeyReMap.end())
|
||||
{
|
||||
// Check if the remap is to a key or a shortcut
|
||||
bool remapToKey = (it->second.index() == 0);
|
||||
|
||||
// If mapped to 0x0 then the key is disabled
|
||||
if (it->second == 0x0)
|
||||
if (remapToKey)
|
||||
{
|
||||
if (std::get<DWORD>(it->second) == 0x0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int key_count = 1;
|
||||
int key_count;
|
||||
if (remapToKey)
|
||||
{
|
||||
key_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
key_count = std::get<Shortcut>(it->second).Size() + 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)
|
||||
DWORD target;
|
||||
if (remapToKey)
|
||||
{
|
||||
target = VK_LWIN;
|
||||
target = KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second));
|
||||
}
|
||||
else
|
||||
{
|
||||
target = KeyboardManagerHelper::FilterArtificialKeys(std::get<Shortcut>(it->second).GetActionKey());
|
||||
}
|
||||
|
||||
// If Ctrl/Alt/Shift is being remapped to Caps Lock, then reset the modifier key state to fix issues in certain IME keyboards where the IME shortcut gets invoked since it detects that the modifier and Caps Lock is pressed even though it is suppressed by the hook - More information at the GitHub issue https://github.com/microsoft/PowerToys/issues/3397
|
||||
if (target == VK_CAPITAL && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN))
|
||||
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, it->first);
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, it->first, target);
|
||||
}
|
||||
|
||||
if (remapToKey)
|
||||
{
|
||||
if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
@@ -49,15 +71,48 @@ namespace KeyboardEventHandlers
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (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)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)targetShortcut.GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
i++;
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerConstants::DUMMY_KEY, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerConstants::DUMMY_KEY, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
i++;
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)targetShortcut.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
UINT res = ii.SendVirtualInput(key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
|
||||
// If Caps Lock is being remapped to Ctrl/Alt/Shift, then reset the modifier key state to fix issues in certain IME keyboards where the IME shortcut gets invoked since it detects that the modifier and Caps Lock is pressed even though it is suppressed by the hook - More information at the GitHub issue https://github.com/microsoft/PowerToys/issues/3397
|
||||
if (it->first == VK_CAPITAL && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN))
|
||||
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, target);
|
||||
if (remapToKey)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, target, it->first);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<DWORD> shortcutKeys = std::get<Shortcut>(it->second).GetKeyCodes();
|
||||
for (auto& itSk : shortcutKeys)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, itSk, it->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -117,7 +172,7 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
|
||||
// Function to a handle a shortcut remap
|
||||
__declspec(dllexport) intptr_t HandleShortcutRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, std::map<Shortcut, RemapShortcut>& reMap, std::mutex& map_mutex, KeyboardManagerState& keyboardManagerState, const std::wstring& activatedApp) noexcept
|
||||
__declspec(dllexport) intptr_t HandleShortcutRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, std::map<Shortcut, RemapShortcut>& reMap, std::vector<Shortcut>& sortedReMapKeys, std::mutex& map_mutex, KeyboardManagerState& keyboardManagerState, const std::wstring& activatedApp) 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<std::mutex> lock(map_mutex);
|
||||
@@ -134,24 +189,28 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
|
||||
// Iterate through the shortcut remaps and apply whichever has been pressed
|
||||
for (auto& it : reMap)
|
||||
for (auto& itShortcut : sortedReMapKeys)
|
||||
{
|
||||
auto& it = reMap.find(itShortcut);
|
||||
|
||||
// If a shortcut is currently in the invoked state then skip till the shortcut that is currently invoked
|
||||
if (isShortcutInvoked && !it.second.isShortcutInvoked)
|
||||
if (isShortcutInvoked && !it->second.isShortcutInvoked)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Check if the remap is to a key or a shortcut
|
||||
bool remapToShortcut = (it->second.targetShortcut.index() == 1);
|
||||
|
||||
const size_t src_size = it.first.Size();
|
||||
const size_t dest_size = it.second.targetShortcut.Size();
|
||||
const size_t src_size = it->first.Size();
|
||||
const size_t dest_size = remapToShortcut ? std::get<Shortcut>(it->second.targetShortcut).Size() : 1;
|
||||
|
||||
// If the shortcut has been pressed down
|
||||
if (!it.second.isShortcutInvoked && it.first.CheckModifiersKeyboardState(ii))
|
||||
if (!it->second.isShortcutInvoked && it->first.CheckModifiersKeyboardState(ii))
|
||||
{
|
||||
if (data->lParam->vkCode == it.first.GetActionKey() && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN))
|
||||
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(ii))
|
||||
// Check if any other keys have been pressed apart from the shortcut. If true, then check for the next shortcut. This is to be done only for shortcut to shortcut remaps
|
||||
if (!it->first.IsKeyboardStateClearExceptShortcut(ii) && remapToShortcut)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -162,15 +221,17 @@ namespace KeyboardEventHandlers
|
||||
// Remember which win key was pressed initially
|
||||
if (ii.GetVirtualKeyState(VK_RWIN))
|
||||
{
|
||||
it.second.winKeyInvoked = ModifierKey::Right;
|
||||
it->second.winKeyInvoked = ModifierKey::Right;
|
||||
}
|
||||
else if (ii.GetVirtualKeyState(VK_LWIN))
|
||||
{
|
||||
it.second.winKeyInvoked = ModifierKey::Left;
|
||||
it->second.winKeyInvoked = ModifierKey::Left;
|
||||
}
|
||||
|
||||
if (remapToShortcut)
|
||||
{
|
||||
// Get the common keys between the two shortcuts
|
||||
int commonKeys = it.first.GetCommonModifiersCount(it.second.targetShortcut);
|
||||
int commonKeys = it->first.GetCommonModifiersCount(std::get<Shortcut>(it->second.targetShortcut));
|
||||
|
||||
// If the original shortcut modifiers are a subset of the new shortcut
|
||||
if (commonKeys == src_size - 1)
|
||||
@@ -180,28 +241,8 @@ namespace KeyboardEventHandlers
|
||||
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);
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
else
|
||||
@@ -215,54 +256,53 @@ namespace KeyboardEventHandlers
|
||||
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++;
|
||||
}
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut));
|
||||
|
||||
// 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);
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
|
||||
it.second.isShortcutInvoked = true;
|
||||
// Modifier state reset might be required for this key depending on the shortcut's action and target modifiers - ex: Win+Caps -> Ctrl+A
|
||||
if (it->first.GetCtrlKey() == NULL && it->first.GetAltKey() == NULL && it->first.GetShiftKey() == NULL)
|
||||
{
|
||||
Shortcut temp = std::get<Shortcut>(it->second.targetShortcut);
|
||||
for (auto keys : temp.GetKeyCodes())
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, keys, data->lParam->vkCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dummy key, key up for all the original shortcut modifier keys and key down for remapped key
|
||||
key_count = 1 + (src_size - 1) + dest_size;
|
||||
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)
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Set target key down state
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
|
||||
// Modifier state reset might be required for this key depending on the shortcut's action and target modifier - ex: Win+Caps -> Ctrl
|
||||
if (it->first.GetCtrlKey() == NULL && it->first.GetAltKey() == NULL && it->first.GetShiftKey() == NULL)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), data->lParam->vkCode);
|
||||
}
|
||||
}
|
||||
|
||||
it->second.isShortcutInvoked = true;
|
||||
// If app specific shortcut is invoked, store the target application
|
||||
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
||||
{
|
||||
@@ -282,18 +322,21 @@ namespace KeyboardEventHandlers
|
||||
// 4. The user presses a modifier key in the original shortcut - suppress that key event since the original shortcut is already held down physically (This case can occur only if a user has a duplicated modifier key (possibly by remapping) or if user presses both L/R versions of a modifier remapped with "Both")
|
||||
// 5. The user presses any key apart from the action key or a modifier key in the original shortcut - revert the keyboard state to just the original modifiers being held down along with the current key press
|
||||
// 6. The user releases any key apart from original modifier or original action key - This can't happen since the key down would have to happen first, which is handled above
|
||||
else if (it.second.isShortcutInvoked)
|
||||
else if (it->second.isShortcutInvoked)
|
||||
{
|
||||
// Get the common keys between the two shortcuts
|
||||
int commonKeys = it.first.GetCommonModifiersCount(it.second.targetShortcut);
|
||||
int commonKeys = remapToShortcut ? it->first.GetCommonModifiersCount(std::get<Shortcut>(it->second.targetShortcut)) : 0;
|
||||
|
||||
// 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))
|
||||
// Case 1: If any of the modifier keys of the original shortcut are released before the action 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;
|
||||
LPINPUT keyEventList;
|
||||
if (remapToShortcut)
|
||||
{
|
||||
// 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))
|
||||
if (std::get<Shortcut>(it->second.targetShortcut).CheckWinKey(data->lParam->vkCode) || std::get<Shortcut>(it->second.targetShortcut).CheckCtrlKey(data->lParam->vkCode) || std::get<Shortcut>(it->second.targetShortcut).CheckAltKey(data->lParam->vkCode) || std::get<Shortcut>(it->second.targetShortcut).CheckShiftKey(data->lParam->vkCode))
|
||||
{
|
||||
// release all new shortcut keys and the common released modifier except the other common modifiers, and add all original shortcut modifiers except the common ones
|
||||
key_count = (dest_size - commonKeys) + (src_size - 1 - commonKeys);
|
||||
@@ -306,67 +349,45 @@ namespace KeyboardEventHandlers
|
||||
|
||||
// If the target shortcut's action key is pressed, then it should be released
|
||||
bool isActionKeyPressed = false;
|
||||
if (GetAsyncKeyState(it.second.targetShortcut.GetActionKey()) & 0x8000)
|
||||
if (GetAsyncKeyState(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()) & 0x8000)
|
||||
{
|
||||
isActionKeyPressed = true;
|
||||
key_count += 1;
|
||||
}
|
||||
|
||||
LPINPUT keyEventList = new INPUT[key_count]();
|
||||
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;
|
||||
if (isActionKeyPressed)
|
||||
{
|
||||
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);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, 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
|
||||
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++;
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut), data->lParam->vkCode);
|
||||
}
|
||||
if ((it.second.targetShortcut.GetCtrlKey() != it.first.GetCtrlKey()) && (!it.first.CheckCtrlKey(data->lParam->vkCode)) && it.first.GetCtrlKey() != NULL)
|
||||
else
|
||||
{
|
||||
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);
|
||||
// 1 for releasing new key and original shortcut modifiers except the one released
|
||||
key_count = dest_size + src_size - 2;
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Release new key state
|
||||
int i = 0;
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
|
||||
// Set original shortcut key down state except the action key and the released modifier since the original action key may or may not be held down. If it is held down it will generate it's own key message
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, Shortcut(), data->lParam->vkCode);
|
||||
}
|
||||
|
||||
it.second.isShortcutInvoked = false;
|
||||
it.second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
||||
{
|
||||
@@ -384,17 +405,24 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
|
||||
// The system will see the modifiers of the new shortcut as being held down because of the shortcut remap
|
||||
if (it.second.targetShortcut.CheckModifiersKeyboardState(ii))
|
||||
if (!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() && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN))
|
||||
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);
|
||||
if (remapToShortcut)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
|
||||
it.second.isShortcutInvoked = true;
|
||||
it->second.isShortcutInvoked = true;
|
||||
lock.unlock();
|
||||
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
@@ -402,14 +430,47 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
|
||||
// Case 3: If the action key is released from the original shortcut keep modifiers of the new shortcut until some other key event which doesn't apply to the original shortcut
|
||||
if (data->lParam->vkCode == it.first.GetActionKey() && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP))
|
||||
if (data->lParam->vkCode == it->first.GetActionKey() && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP))
|
||||
{
|
||||
size_t key_count = 1;
|
||||
LPINPUT keyEventList = new INPUT[key_count]();
|
||||
LPINPUT keyEventList;
|
||||
if (remapToShortcut)
|
||||
{
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)it.second.targetShortcut.GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
it->second.isShortcutInvoked = true;
|
||||
}
|
||||
|
||||
// for remap from shortcut to key, when the action key is released, the remap invoke is completed so revert to original shortcut state
|
||||
else
|
||||
{
|
||||
// 1 for releasing new key and original shortcut modifiers, and dummy key
|
||||
key_count = dest_size + src_size;
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Release new key state
|
||||
int i = 0;
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
|
||||
// Set original shortcut key down state except the action key and the released modifier
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// 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;
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
||||
{
|
||||
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
|
||||
}
|
||||
}
|
||||
|
||||
it.second.isShortcutInvoked = true;
|
||||
lock.unlock();
|
||||
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
@@ -417,15 +478,32 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
|
||||
// Case 4: If a modifier key in the original shortcut is pressed then suppress that key event since the original shortcut is already held down physically - This case can occur only if a user has a duplicated modifier key (possibly by remapping) or if user presses both L/R versions of a modifier remapped with "Both"
|
||||
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_KEYDOWN || data->wParam == WM_SYSKEYDOWN))
|
||||
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_KEYDOWN || data->wParam == WM_SYSKEYDOWN))
|
||||
{
|
||||
it.second.isShortcutInvoked = true;
|
||||
if (remapToShortcut)
|
||||
{
|
||||
it->second.isShortcutInvoked = true;
|
||||
|
||||
// Modifier state reset might be required for this key depending on the target shortcut action key - ex: Ctrl+A -> Win+Caps
|
||||
if (std::get<Shortcut>(it->second.targetShortcut).GetCtrlKey() == NULL && std::get<Shortcut>(it->second.targetShortcut).GetAltKey() == NULL && std::get<Shortcut>(it->second.targetShortcut).GetShiftKey() == NULL)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Case 5: If any key apart from the action key or a modifier key in the original shortcut is pressed then revert the keyboard state to just the original modifiers being held down along with the current key press
|
||||
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
|
||||
{
|
||||
if (remapToShortcut)
|
||||
{
|
||||
// Modifier state reset might be required for this key depending on the target shortcut action key - ex: Ctrl+A -> Win+Caps, Shift is pressed. System should not see Shift and Caps pressed together
|
||||
if (std::get<Shortcut>(it->second.targetShortcut).GetCtrlKey() == NULL && std::get<Shortcut>(it->second.targetShortcut).GetAltKey() == NULL && std::get<Shortcut>(it->second.targetShortcut).GetShiftKey() == NULL)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
|
||||
}
|
||||
|
||||
size_t key_count;
|
||||
LPINPUT keyEventList;
|
||||
|
||||
@@ -436,7 +514,7 @@ namespace KeyboardEventHandlers
|
||||
|
||||
// If the target shortcut's action key is pressed, then it should be released and original shortcut's action key should be set
|
||||
bool isActionKeyPressed = false;
|
||||
if (GetAsyncKeyState(it.second.targetShortcut.GetActionKey()) & 0x8000)
|
||||
if (GetAsyncKeyState(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()) & 0x8000)
|
||||
{
|
||||
isActionKeyPressed = true;
|
||||
key_count += 2;
|
||||
@@ -448,34 +526,15 @@ namespace KeyboardEventHandlers
|
||||
int i = 0;
|
||||
if (isActionKeyPressed)
|
||||
{
|
||||
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);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
|
||||
// key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again
|
||||
if (isActionKeyPressed)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -494,7 +553,7 @@ namespace KeyboardEventHandlers
|
||||
|
||||
// If the target shortcut's action key is pressed, then it should be released and original shortcut's action key should be set
|
||||
bool isActionKeyPressed = false;
|
||||
if (GetAsyncKeyState(it.second.targetShortcut.GetActionKey()) & 0x8000)
|
||||
if (GetAsyncKeyState(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()) & 0x8000)
|
||||
{
|
||||
isActionKeyPressed = true;
|
||||
key_count += 2;
|
||||
@@ -507,57 +566,18 @@ namespace KeyboardEventHandlers
|
||||
int i = 0;
|
||||
if (isActionKeyPressed)
|
||||
{
|
||||
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);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
|
||||
// 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++;
|
||||
}
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, 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)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it.first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -570,8 +590,8 @@ namespace KeyboardEventHandlers
|
||||
i++;
|
||||
}
|
||||
|
||||
it.second.isShortcutInvoked = false;
|
||||
it.second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
||||
{
|
||||
@@ -582,17 +602,9 @@ namespace KeyboardEventHandlers
|
||||
delete[] keyEventList;
|
||||
return 1;
|
||||
}
|
||||
// Case 6: If any key apart from original modifier or original action key is released - This can't happen since the key down would have to happen first, which is handled above
|
||||
}
|
||||
|
||||
// Code added for safety: Should not generally occur unless some weird keyboard interaction occurs
|
||||
// If it was in isShortcutInvoked state and none of the above cases occur, then reset the flags
|
||||
it.second.isShortcutInvoked = false;
|
||||
it.second.winKeyInvoked = ModifierKey::Disabled;
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
||||
{
|
||||
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
|
||||
// For remap to key, nothing should be done since the shortcut should only get released on releasing any of the original shortcut keys.
|
||||
// Case 6: If any key apart from original modifier or original action key is released - This can't happen since the key down would have to happen first, which is handled above. If a key up message is generated for some other key (maybe by code) do not suppress it
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -606,7 +618,7 @@ namespace KeyboardEventHandlers
|
||||
// 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(ii, data, keyboardManagerState.osLevelShortcutReMap, keyboardManagerState.osLevelShortcutReMap_mutex, keyboardManagerState);
|
||||
bool result = HandleShortcutRemapEvent(ii, data, keyboardManagerState.osLevelShortcutReMap, keyboardManagerState.osLevelShortcutReMapSortedKeys, keyboardManagerState.osLevelShortcutReMap_mutex, keyboardManagerState);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -664,7 +676,7 @@ namespace KeyboardEventHandlers
|
||||
if (it != keyboardManagerState.appSpecificShortcutReMap.end())
|
||||
{
|
||||
lock.unlock();
|
||||
bool result = HandleShortcutRemapEvent(ii, data, keyboardManagerState.appSpecificShortcutReMap[query_string], keyboardManagerState.appSpecificShortcutReMap_mutex, keyboardManagerState, query_string);
|
||||
bool result = HandleShortcutRemapEvent(ii, data, keyboardManagerState.appSpecificShortcutReMap[query_string], keyboardManagerState.appSpecificShortcutReMapSortedKeys[query_string], keyboardManagerState.appSpecificShortcutReMap_mutex, keyboardManagerState, query_string);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -688,8 +700,11 @@ namespace KeyboardEventHandlers
|
||||
delete[] keyEventList;
|
||||
}
|
||||
|
||||
// Function to ensure Ctrl/Shift/Alt modifier key state is not detected as pressed down by applications which detect keys at a lower level than hooks when it is remapped
|
||||
void ResetIfModifierKeyForLowerLevelKeyHandlers(InputInterface& ii, DWORD key)
|
||||
// Function to ensure Ctrl/Shift/Alt modifier key state is not detected as pressed down by applications which detect keys at a lower level than hooks when it is remapped for scenarios where its required
|
||||
void ResetIfModifierKeyForLowerLevelKeyHandlers(InputInterface& ii, DWORD key, DWORD target)
|
||||
{
|
||||
// If the target is Caps Lock and the other key is either Ctrl/Alt/Shift then reset the modifier state to lower level handlers
|
||||
if (target == VK_CAPITAL)
|
||||
{
|
||||
// If the argument is either of the Ctrl/Shift/Alt modifier key codes
|
||||
if (KeyboardManagerHelper::IsModifierKey(key) && !(key == VK_LWIN || key == VK_RWIN || key == CommonSharedConstants::VK_WIN_BOTH))
|
||||
@@ -705,3 +720,4 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace KeyboardEventHandlers
|
||||
__declspec(dllexport) intptr_t HandleSingleKeyToggleToModEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept;
|
||||
|
||||
// Function to a handle a shortcut remap
|
||||
__declspec(dllexport) intptr_t HandleShortcutRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, std::map<Shortcut, RemapShortcut>& reMap, std::mutex& map_mutex, KeyboardManagerState& keyboardManagerState, const std::wstring& activatedApp = KeyboardManagerConstants::NoActivatedApp) noexcept;
|
||||
__declspec(dllexport) intptr_t HandleShortcutRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, std::map<Shortcut, RemapShortcut>& reMap, std::vector<Shortcut>& sortedReMapKeys, std::mutex& map_mutex, KeyboardManagerState& keyboardManagerState, const std::wstring& activatedApp = KeyboardManagerConstants::NoActivatedApp) noexcept;
|
||||
|
||||
// Function to a handle an os-level shortcut remap
|
||||
__declspec(dllexport) intptr_t HandleOSLevelShortcutRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept;
|
||||
@@ -29,6 +29,6 @@ namespace KeyboardEventHandlers
|
||||
// Function to ensure Num Lock state does not change when it is suppressed by the low level hook
|
||||
void SetNumLockToPreviousState(InputInterface& ii);
|
||||
|
||||
// Function to ensure Ctrl/Shift/Alt modifier key state is not detected as pressed down by applications which detect keys at a lower level than hooks when it is remapped
|
||||
void ResetIfModifierKeyForLowerLevelKeyHandlers(InputInterface& ii, DWORD key);
|
||||
// Function to ensure Ctrl/Shift/Alt modifier key state is not detected as pressed down by applications which detect keys at a lower level than hooks when it is remapped for scenarios where its required
|
||||
void ResetIfModifierKeyForLowerLevelKeyHandlers(InputInterface& ii, DWORD key, DWORD target);
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <common/settings_helpers.h>
|
||||
#include <common/debug_control.h>
|
||||
#include <keyboardmanager/common/trace.h>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include "KeyboardEventHandlers.h"
|
||||
#include "Input.h"
|
||||
|
||||
@@ -105,8 +106,19 @@ public:
|
||||
{
|
||||
auto originalKey = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
|
||||
auto newRemapKey = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName);
|
||||
|
||||
// If remapped to a shortcut
|
||||
if (std::wstring(newRemapKey).find(L";") != std::string::npos)
|
||||
{
|
||||
keyboardManagerState.AddSingleKeyRemap(std::stoul(originalKey.c_str()), Shortcut(newRemapKey.c_str()));
|
||||
}
|
||||
|
||||
// If remapped to a key
|
||||
else
|
||||
{
|
||||
keyboardManagerState.AddSingleKeyRemap(std::stoul(originalKey.c_str()), std::stoul(newRemapKey.c_str()));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Improper Key Data JSON. Try the next remap.
|
||||
@@ -137,9 +149,18 @@ public:
|
||||
{
|
||||
auto originalKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
|
||||
auto newRemapKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName);
|
||||
Shortcut originalSC(originalKeys.c_str());
|
||||
Shortcut newRemapSC(newRemapKeys.c_str());
|
||||
keyboardManagerState.AddOSLevelShortcut(originalSC, newRemapSC);
|
||||
|
||||
// If remapped to a shortcut
|
||||
if (std::wstring(newRemapKeys).find(L";") != std::string::npos)
|
||||
{
|
||||
keyboardManagerState.AddOSLevelShortcut(Shortcut(originalKeys.c_str()), Shortcut(newRemapKeys.c_str()));
|
||||
}
|
||||
|
||||
// If remapped to a key
|
||||
else
|
||||
{
|
||||
keyboardManagerState.AddOSLevelShortcut(Shortcut(originalKeys.c_str()), std::stoul(newRemapKeys.c_str()));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -163,9 +184,18 @@ public:
|
||||
auto originalKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
|
||||
auto newRemapKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName);
|
||||
auto targetApp = it.GetObjectW().GetNamedString(KeyboardManagerConstants::TargetAppSettingName);
|
||||
Shortcut originalSC(originalKeys.c_str());
|
||||
Shortcut newRemapSC(newRemapKeys.c_str());
|
||||
keyboardManagerState.AddAppSpecificShortcut(targetApp.c_str(), originalSC, newRemapSC);
|
||||
|
||||
// If remapped to a shortcut
|
||||
if (std::wstring(newRemapKeys).find(L";") != std::string::npos)
|
||||
{
|
||||
keyboardManagerState.AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), Shortcut(newRemapKeys.c_str()));
|
||||
}
|
||||
|
||||
// If remapped to a key
|
||||
else
|
||||
{
|
||||
keyboardManagerState.AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), std::stoul(newRemapKeys.c_str()));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -390,6 +420,17 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the Detect Shortcut Window from Remap Keys is currently activated, then suppress the keyboard event
|
||||
KeyboardManagerHelper::KeyboardHookDecision remapKeyShortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data, true);
|
||||
if (remapKeyShortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (remapKeyShortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Remap a key
|
||||
intptr_t SingleKeyRemapResult = KeyboardEventHandlers::HandleSingleKeyRemapEvent(inputHandler, data, keyboardManagerState);
|
||||
|
||||
@@ -400,7 +441,7 @@ public:
|
||||
}
|
||||
|
||||
// If the Detect Shortcut Window is currently activated, then suppress the keyboard event
|
||||
KeyboardManagerHelper::KeyboardHookDecision shortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data);
|
||||
KeyboardManagerHelper::KeyboardHookDecision shortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data, false);
|
||||
if (shortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress)
|
||||
{
|
||||
return 1;
|
||||
|
||||
@@ -5,3 +5,4 @@
|
||||
#include <shlwapi.h>
|
||||
#include <stdexcept>
|
||||
#include <unordered_set>
|
||||
#include <winrt/base.h>
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
|
||||
namespace RemappingLogicTests
|
||||
{
|
||||
TEST_CLASS (AppSpecificShortcutRemappingTests)
|
||||
@@ -86,7 +85,7 @@ namespace RemappingLogicTests
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl and A key states should be unchanged, Alt and V key states should be true
|
||||
// Ctrl and A key states should be true, Alt and V key states should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
@@ -172,12 +171,146 @@ namespace RemappingLogicTests
|
||||
// Release A then Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
|
||||
// Ctrl, A, Alt and Tab should all be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_TAB), false);
|
||||
}
|
||||
|
||||
// Test if the app specific shortcut to key remap takes place when the target app is in foreground
|
||||
TEST_METHOD (AppSpecificShortcutToSingleKey_ShouldGetRemapped_WhenAppIsInForeground)
|
||||
{
|
||||
// Remap Ctrl+A to V
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddAppSpecificShortcut(testApp1, src, 0x56);
|
||||
|
||||
// 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;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl and A key states should be unchanged, V key states should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true);
|
||||
}
|
||||
|
||||
// Test if the app specific shortcut to key remap takes place when the target app is not in foreground
|
||||
TEST_METHOD (AppSpecificShortcutToSingleKey_ShouldNotGetRemapped_WhenAppIsNotInForeground)
|
||||
{
|
||||
// Remap Ctrl+A to V
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddAppSpecificShortcut(testApp1, src, 0x56);
|
||||
|
||||
// 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;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl and A key states should be true, V key state should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
}
|
||||
|
||||
// Test if the the keyboard manager state's activated app is correctly set after an app specific shortcut to key remap takes place
|
||||
TEST_METHOD (AppSpecificShortcutToSingleKey_ShouldSetCorrectActivatedApp_WhenRemapOccurs)
|
||||
{
|
||||
// Remap Ctrl+A to V
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddAppSpecificShortcut(testApp1, src, 0x56);
|
||||
|
||||
// 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;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// 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;
|
||||
|
||||
// Release A then Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Activated app should be empty string
|
||||
Assert::AreEqual(std::wstring(KeyboardManagerConstants::NoActivatedApp), testState.GetActivatedApp());
|
||||
}
|
||||
// Test if the key states get cleared if foreground app changes after app-specific shortcut to key shortcut is invoked and then released
|
||||
TEST_METHOD (AppSpecificShortcutToSingleKey_ShouldClearKeyStates_WhenForegroundAppChangesAfterShortcutIsPressedOnRelease)
|
||||
{
|
||||
// Remap Ctrl+A to V
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddAppSpecificShortcut(testApp1, src, 0x56);
|
||||
|
||||
// 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;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// 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;
|
||||
|
||||
// Release A then Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl, A, V should all be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,7 +25,16 @@ namespace RemappingLogicTests
|
||||
|
||||
// Set HandleOSLevelShortcutRemapEvent as the hook procedure
|
||||
std::function<intptr_t(LowlevelKeyboardEvent*)> currentHookProc = std::bind(&KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState));
|
||||
mockedInputHandler.SetHookProc(currentHookProc);
|
||||
mockedInputHandler.SetHookProc([currentHookProc](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
||||
{
|
||||
return currentHookProc(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (intptr_t)1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Test if correct keyboard states are set for a 2 key shortcut remap wih different modifiers key down
|
||||
@@ -1105,5 +1114,895 @@ namespace RemappingLogicTests
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x58), true);
|
||||
}
|
||||
|
||||
// Test if correct keyboard states are set for a 2 key shortcut to a single key remap not containing that key on key down followed by key up
|
||||
TEST_METHOD (RemappedTwoKeyShortcutToSingleKeyNotContainingThatKey_ShouldSetCorrectKeyStates_OnKeyEvents)
|
||||
{
|
||||
// Remap Ctrl+A to Alt
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_MENU);
|
||||
|
||||
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;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl, A should be false, Alt should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), true);
|
||||
|
||||
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;
|
||||
|
||||
// Release Ctrl+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl, A, Alt should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
}
|
||||
|
||||
// Test if correct keyboard states are set for a 3 key shortcut to a single key remap not containing that key on key down followed by key up
|
||||
TEST_METHOD (RemappedThreeKeyShortcutToSingleKeyNotContainingThatKey_ShouldSetCorrectKeyStates_OnKeyEvents)
|
||||
{
|
||||
// Remap Ctrl+Shift+A to Alt
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(VK_SHIFT);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_MENU);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_SHIFT;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
|
||||
// Send Ctrl+Shift+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl, Shift, A should be false, Alt should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), true);
|
||||
|
||||
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_SHIFT;
|
||||
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = VK_CONTROL;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Release Ctrl+Shift+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl, Shift, A, Alt should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
}
|
||||
|
||||
// Test if correct keyboard states are set for a 2 key shortcut to a single key remap containing that key on key down followed by key up
|
||||
TEST_METHOD (RemappedTwoKeyShortcutToSingleKeyContainingThatKey_ShouldSetCorrectKeyStates_OnKeyEvents)
|
||||
{
|
||||
// Remap Ctrl+A to Ctrl
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_CONTROL);
|
||||
|
||||
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;
|
||||
|
||||
// Send Ctrl+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A should be false, Ctrl should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
|
||||
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;
|
||||
|
||||
// Release Ctrl+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl, A should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
}
|
||||
|
||||
// Test if correct keyboard states are set for a 3 key shortcut to a single key remap containing that key on key down followed by key up
|
||||
TEST_METHOD (RemappedThreeKeyShortcutToSingleKeyContainingThatKey_ShouldSetCorrectKeyStates_OnKeyEvents)
|
||||
{
|
||||
// Remap Ctrl+Shift+A to Ctrl
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(VK_SHIFT);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_CONTROL);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_SHIFT;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
|
||||
// Send Ctrl+Shift+A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Shift, A should be false, Ctrl should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
|
||||
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_SHIFT;
|
||||
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = VK_CONTROL;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Release Ctrl+Shift+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl, Shift, A should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
}
|
||||
|
||||
// Test if keyboard state is reverted for a shortcut to a single key remap (target key is not a part of the shortcut) on key down followed by releasing the action key
|
||||
TEST_METHOD (RemappedShortcutToSingleKeyWhereKeyIsNotInShortcut_ShouldSetOriginalModifier_OnReleasingActionKey)
|
||||
{
|
||||
// Remap Ctrl+A to Alt
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_MENU);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Press Ctrl+A, release A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A, Alt should be false, Ctrl should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
}
|
||||
|
||||
// Test if keyboard state is reverted for a shortcut to a single key remap (target key is a modifier in the shortcut) on key down followed by releasing the action key
|
||||
TEST_METHOD (RemappedShortcutToSingleKeyWhereKeyIsAModifierInShortcut_ShouldSetOriginalModifier_OnReleasingActionKey)
|
||||
{
|
||||
// Remap Ctrl+A to Ctrl
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_CONTROL);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Press Ctrl+A, release A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A should be false, Ctrl should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
}
|
||||
|
||||
// Test if keyboard state is reverted for a shortcut to a single key remap (target key is the action key in the shortcut) on key down followed by releasing the action key
|
||||
TEST_METHOD (RemappedShortcutToSingleKeyWhereKeyIsActionKeyInShortcut_ShouldSetOriginalModifier_OnReleasingActionKey)
|
||||
{
|
||||
// Remap Ctrl+A to A
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, 0x41);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Press Ctrl+A, release A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A should be false, Ctrl should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
}
|
||||
|
||||
// Test if keyboard state is reverted for a shortcut to a single key remap (target key is not a part of the shortcut) on key down followed by releasing the modifier key
|
||||
TEST_METHOD (RemappedShortcutToSingleKeyWhereKeyIsNotInShortcut_ShouldSetOriginalModifier_OnReleasingModifierKey)
|
||||
{
|
||||
// Remap Ctrl+A to Alt
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_MENU);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = VK_CONTROL;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Press Ctrl+A, release Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A, Alt, Ctrl should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
}
|
||||
|
||||
// Test if keyboard state is reverted for a shortcut to a single key remap (target key is a modifier in the shortcut) on key down followed by releasing the modifier key
|
||||
TEST_METHOD (RemappedShortcutToSingleKeyWhereKeyIsAModifierInShortcut_ShouldSetOriginalModifier_OnReleasingModifierKey)
|
||||
{
|
||||
// Remap Ctrl+A to Ctrl
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_CONTROL);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = VK_CONTROL;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Press Ctrl+A, release Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A, Ctrl should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
}
|
||||
|
||||
// Test if keyboard state is reverted for a shortcut to a single key remap (target key is the action key in the shortcut) on key down followed by releasing the modifier key
|
||||
TEST_METHOD (RemappedShortcutToSingleKeyWhereKeyIsActionKeyInShortcut_ShouldSetOriginalModifier_ModifierKey)
|
||||
{
|
||||
// Remap Ctrl+A to A
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, 0x41);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = VK_CONTROL;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Press Ctrl+A, release Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A, Ctrl should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
}
|
||||
|
||||
// Test if remap is invoked for a shortcut to a single key remap when the shortcut is invoked along with other keys pressed before it
|
||||
TEST_METHOD (RemappedShortcutToSingleKey_ShouldBeInvoked_IfOtherKeysArePressedAlongWithIt)
|
||||
{
|
||||
// Remap Ctrl+A to Alt
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_MENU);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x42;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_CONTROL;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
|
||||
// Press B+Ctrl+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A, Ctrl should be false, B, Alt should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
}
|
||||
|
||||
// Test that remap is not invoked for a shortcut to a single key remap when a larger remapped shortcut to shortcut containing those shortcut keys is invoked
|
||||
TEST_METHOD (RemappedShortcutToSingleKey_ShouldNotBeInvoked_IfALargerRemappedShortcutToShortcutContainingThoseShortcutKeysIsInvoked)
|
||||
{
|
||||
// Remap Ctrl+A to Alt
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_MENU);
|
||||
// Remap Shift+Ctrl+A to Ctrl+V
|
||||
src.SetKey(VK_SHIFT);
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_CONTROL);
|
||||
dest.SetKey(0x56);
|
||||
testState.AddOSLevelShortcut(src, dest);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_SHIFT;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_CONTROL;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
|
||||
// Press Shift+Ctrl+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Alt, A, Shift should be false, Ctrl, V should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true);
|
||||
}
|
||||
|
||||
// Test that remap is not invoked for a shortcut to a single key remap when a larger remapped shortcut to key containing those shortcut keys is invoked
|
||||
TEST_METHOD (RemappedShortcutToSingleKey_ShouldNotBeInvoked_IfALargerRemappedShortcutToKeyContainingThoseShortcutKeysIsInvoked)
|
||||
{
|
||||
// Remap Ctrl+A to Alt
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_MENU);
|
||||
// Remap Shift+Ctrl+A to B
|
||||
src.SetKey(VK_SHIFT);
|
||||
testState.AddOSLevelShortcut(src, 0x42);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_SHIFT;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_CONTROL;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
|
||||
// Press Shift+Ctrl+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Alt, Ctrl, A, Shift should be false, B should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
}
|
||||
|
||||
// Test if remap is invoked for a shortcut to a single key remap when the shortcut is invoked along with other keys pressed after it and then action key is released
|
||||
TEST_METHOD (RemappedShortcutToSingleKey_ShouldBeInvoked_IfOtherKeysArePressedAfterItAndActionKeyIsReleased)
|
||||
{
|
||||
// Remap Ctrl+A to Alt
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_MENU);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x42;
|
||||
|
||||
// Press Ctrl+A+B
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A, Ctrl should be false, B, Alt should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = 0x41;
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Release A
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
|
||||
// A, Alt should be false, Ctrl, B should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
}
|
||||
|
||||
// Test if remap is invoked for a shortcut to a single key remap when the shortcut is invoked along with other keys pressed after it and modifier key is released
|
||||
TEST_METHOD (RemappedShortcutToSingleKey_ShouldBeInvoked_IfOtherKeysArePressedAfterItAndModifierKeyIsReleased)
|
||||
{
|
||||
// Remap Ctrl+A to Alt
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, VK_MENU);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x42;
|
||||
|
||||
// Press Ctrl+A+B
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A, Ctrl should be false, B, Alt should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Release Ctrl
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl, Alt, A should be false, B should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x42), true);
|
||||
}
|
||||
|
||||
// Test if Windows left key state is set when a shortcut remap to Win both is invoked
|
||||
TEST_METHOD (RemappedShortcutToWinBoth_ShouldSetLWinKeyState_OnKeyEvent)
|
||||
{
|
||||
// Remap Ctrl+A to Win both
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, CommonSharedConstants::VK_WIN_BOTH);
|
||||
|
||||
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;
|
||||
|
||||
// Press Ctrl+A
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A, Ctrl should be false, LWin should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LWIN), true);
|
||||
|
||||
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;
|
||||
|
||||
// Release A, Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Ctrl, A, LWin should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
}
|
||||
|
||||
// Test if invoking two remapped shortcuts that share modifiers, where the first one remaps to a key and the second one remaps to a shortcut, in succession sets the correct keyboard states
|
||||
TEST_METHOD (TwoRemappedShortcutsThatShareModifiersWhereFirstOneRemapsToAKeyAndSecondOneRemapsToAShortcut_ShouldSetRemappedKeyStates_OnPressingSecondShortcutActionKeyAfterInvokingFirstShortcutRemap)
|
||||
{
|
||||
// Remap Alt+A to D
|
||||
Shortcut src;
|
||||
src.SetKey(VK_MENU);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, 0x44);
|
||||
|
||||
// Remap Alt+V to Ctrl+X
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_MENU);
|
||||
src1.SetKey(0x56);
|
||||
Shortcut dest1;
|
||||
dest1.SetKey(VK_CONTROL);
|
||||
dest1.SetKey(0x58);
|
||||
testState.AddOSLevelShortcut(src1, dest1);
|
||||
|
||||
const int nInputs = 4;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_MENU;
|
||||
input[0].ki.dwFlags = 0;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[1].ki.dwFlags = 0;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input[3].type = INPUT_KEYBOARD;
|
||||
input[3].ki.wVk = 0x56;
|
||||
input[3].ki.dwFlags = 0;
|
||||
|
||||
// Send Alt+A, release A, press V
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Alt, A, D, V key states should be unchanged, Ctrl, X should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x44), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x58), true);
|
||||
}
|
||||
|
||||
// Test if invoking two remapped shortcuts that share modifiers, where the first one remaps to a key and the second one remaps to a key, in succession sets the correct keyboard states
|
||||
TEST_METHOD (TwoRemappedShortcutsThatShareModifiersWhereFirstOneRemapsToAKeyAndSecondOneRemapsToAKey_ShouldSetRemappedKeyStates_OnPressingSecondShortcutActionKeyAfterInvokingFirstShortcutRemap)
|
||||
{
|
||||
// Remap Alt+A to D
|
||||
Shortcut src;
|
||||
src.SetKey(VK_MENU);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, 0x44);
|
||||
|
||||
// Remap Alt+V to X
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_MENU);
|
||||
src1.SetKey(0x56);
|
||||
testState.AddOSLevelShortcut(src1, 0x58);
|
||||
|
||||
const int nInputs = 4;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_MENU;
|
||||
input[0].ki.dwFlags = 0;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[1].ki.dwFlags = 0;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input[3].type = INPUT_KEYBOARD;
|
||||
input[3].ki.wVk = 0x56;
|
||||
input[3].ki.dwFlags = 0;
|
||||
|
||||
// Send Alt+A, release A, press V
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Alt, A, D, V key states should be unchanged, X should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x44), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x58), true);
|
||||
}
|
||||
|
||||
// Test if invoking two remapped shortcuts that share modifiers, where the first one remaps to a shortcut and the second one remaps to a key, in succession sets the correct keyboard states
|
||||
TEST_METHOD (TwoRemappedShortcutsThatShareModifiersWhereFirstOneRemapsToAShortcutAndSecondOneRemapsToAKey_ShouldSetRemappedKeyStates_OnPressingSecondShortcutActionKeyAfterInvokingFirstShortcutRemap)
|
||||
{
|
||||
// Remap Alt+A to Ctrl+C
|
||||
Shortcut src;
|
||||
src.SetKey(VK_MENU);
|
||||
src.SetKey(0x41);
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_CONTROL);
|
||||
dest.SetKey(0x43);
|
||||
testState.AddOSLevelShortcut(src, dest);
|
||||
|
||||
// Remap Alt+V to X
|
||||
Shortcut src1;
|
||||
src1.SetKey(VK_MENU);
|
||||
src1.SetKey(0x56);
|
||||
testState.AddOSLevelShortcut(src1, 0x58);
|
||||
|
||||
const int nInputs = 4;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_MENU;
|
||||
input[0].ki.dwFlags = 0;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[1].ki.dwFlags = 0;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input[3].type = INPUT_KEYBOARD;
|
||||
input[3].ki.wVk = 0x56;
|
||||
input[3].ki.dwFlags = 0;
|
||||
|
||||
// Send Alt+A, release A, press V
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// Alt, A, C, V, Ctrl key states should be unchanged, X should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x44), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x58), true);
|
||||
}
|
||||
|
||||
// Test if correct keyboard states are set if a shortcut to single key remap is pressed and then an unremapped shortcut with the same modifier is pressed - Ex: Ctrl+A is remapped. User invokes Ctrl+A then releases A and presses C (while Ctrl is held), should invoke Ctrl+C
|
||||
TEST_METHOD (InvokingUnremappedShortcutAfterRemappedShortcutToSingleKeyWithSameModifier_ShouldSetUnremappedShortcut_OnKeyDown)
|
||||
{
|
||||
// Remap Ctrl+A to V
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, 0x56);
|
||||
|
||||
const int nInputs = 4;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x41;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input[3].type = INPUT_KEYBOARD;
|
||||
input[3].ki.wVk = 0x43;
|
||||
|
||||
// Send Ctrl+A keydown, A key up, then C key down
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A, V key states should be unchanged, Ctrl, C should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x43), true);
|
||||
}
|
||||
|
||||
// Test if SendVirtualInput is sent exactly once with the suppress flag when Win+CapsLock is remapped to shortcut containing Ctrl
|
||||
TEST_METHOD (HandleShortcutRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenWinCapsLockIsMappedToShortcutContainingCtrl)
|
||||
{
|
||||
// Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remap Win+CapsLock to Ctrl+A
|
||||
Shortcut src;
|
||||
src.SetKey(CommonSharedConstants::VK_WIN_BOTH);
|
||||
src.SetKey(VK_CAPITAL);
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_CONTROL);
|
||||
dest.SetKey(0x41);
|
||||
testState.AddOSLevelShortcut(src, dest);
|
||||
|
||||
const int nInputs = 2;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_LWIN;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_CAPITAL;
|
||||
|
||||
// Send LWin+CapsLock keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly once with the above condition
|
||||
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
}
|
||||
|
||||
// Test if SendVirtualInput is sent exactly once with the suppress flag when Win+CapsLock is remapped to Ctrl
|
||||
TEST_METHOD (HandleShortcutRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenWinCapsLockIsMappedToCtrl)
|
||||
{
|
||||
// Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remap Win+CapsLock to Ctrl+A
|
||||
Shortcut src;
|
||||
src.SetKey(CommonSharedConstants::VK_WIN_BOTH);
|
||||
src.SetKey(VK_CAPITAL);
|
||||
testState.AddOSLevelShortcut(src, VK_CONTROL);
|
||||
|
||||
const int nInputs = 2;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_LWIN;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = VK_CAPITAL;
|
||||
|
||||
// Send LWin+CapsLock keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly once with the above condition
|
||||
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
}
|
||||
|
||||
// Test if SendVirtualInput is sent exactly once with the suppress flag when shortcut containing Ctrl is remapped to shortcut Win+CapsLock and Ctrl is pressed again while shortcut remap is invoked
|
||||
TEST_METHOD (HandleShortcutRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenShortcutContainingCtrlIsMappedToWinCapsLockAndCtrlIsPressedWhileInvoked)
|
||||
{
|
||||
// Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remap Ctrl+A to Win+CapsLock
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
Shortcut dest;
|
||||
dest.SetKey(CommonSharedConstants::VK_WIN_BOTH);
|
||||
dest.SetKey(VK_CAPITAL);
|
||||
testState.AddOSLevelShortcut(src, dest);
|
||||
|
||||
const int nInputs = 3;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = VK_CONTROL;
|
||||
|
||||
// Send LWin+CapsLock keydown followed by Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly once with the above condition
|
||||
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
}
|
||||
|
||||
// Test if SendVirtualInput is sent exactly once with the suppress flag when shortcut containing Ctrl is remapped to shortcut Win+CapsLock and Shift is pressed again while shortcut remap is invoked
|
||||
TEST_METHOD (HandleShortcutRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenShortcutContainingCtrlIsMappedToWinCapsLockAndShiftIsPressedWhileInvoked)
|
||||
{
|
||||
// Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remap Ctrl+A to Win+CapsLock
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
Shortcut dest;
|
||||
dest.SetKey(CommonSharedConstants::VK_WIN_BOTH);
|
||||
dest.SetKey(VK_CAPITAL);
|
||||
testState.AddOSLevelShortcut(src, dest);
|
||||
|
||||
const int nInputs = 3;
|
||||
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = VK_SHIFT;
|
||||
|
||||
// Send LWin+CapsLock keydown followed by Ctrl
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly once with the above condition
|
||||
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
}
|
||||
|
||||
// Test that the shortcut remap state is not reset when an unrelated key up message is sent - required to handle programs sending dummy key up messages
|
||||
TEST_METHOD (ShortcutRemap_ShouldNotGetReset_OnSendingKeyUpForAKeyNotPresentInTheShortcutAfterInvokingTheShortcut)
|
||||
{
|
||||
// Remap Ctrl+A to Ctrl+V
|
||||
Shortcut src;
|
||||
src.SetKey(VK_CONTROL);
|
||||
src.SetKey(0x41);
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_CONTROL);
|
||||
dest.SetKey(0x56);
|
||||
testState.AddOSLevelShortcut(src, dest);
|
||||
|
||||
const int nInputs = 3;
|
||||
INPUT input[nInputs] = {};
|
||||
input[0].type = INPUT_KEYBOARD;
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
input[1].type = INPUT_KEYBOARD;
|
||||
input[1].ki.wVk = 0x41;
|
||||
input[2].type = INPUT_KEYBOARD;
|
||||
input[2].ki.wVk = 0x42;
|
||||
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Send Ctrl+A keydown, then B key up
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, Ctrl, V should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true);
|
||||
|
||||
// Shortcut invoked state should be true
|
||||
Assert::AreEqual(true, testState.osLevelShortcutReMap[src].isShortcutInvoked);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "MockedInput.h"
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/dll/KeyboardEventHandlers.h>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include "TestHelpers.h"
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace RemappingLogicTests
|
||||
input[0].ki.wVk = 0x41;
|
||||
|
||||
// Send A keydown
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, and B key state should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
@@ -48,7 +48,7 @@ namespace RemappingLogicTests
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Send A keyup
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, and B key state should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
@@ -67,14 +67,14 @@ namespace RemappingLogicTests
|
||||
input[0].ki.wVk = 0x41;
|
||||
|
||||
// Send A keydown
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Send A keyup
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
@@ -92,7 +92,7 @@ namespace RemappingLogicTests
|
||||
input[0].ki.wVk = 0x41;
|
||||
|
||||
// Send A keydown
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, and common Win key state should be true
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
@@ -100,7 +100,7 @@ namespace RemappingLogicTests
|
||||
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
// Send A keyup
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, and common Win key state should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
@@ -108,7 +108,7 @@ namespace RemappingLogicTests
|
||||
}
|
||||
|
||||
// Test if SendVirtualInput is sent exactly once with the suppress flag when Caps Lock is remapped to Ctrl
|
||||
TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirutalInputWithSuppressFlagExactlyOnce_WhenCapsLockIsMappedToCtrlAltShift)
|
||||
TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenCapsLockIsMappedToCtrlAltShift)
|
||||
{
|
||||
// Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) {
|
||||
@@ -127,14 +127,14 @@ namespace RemappingLogicTests
|
||||
input[0].ki.wVk = VK_CAPITAL;
|
||||
|
||||
// Send Caps Lock keydown
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly once with the above condition
|
||||
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
}
|
||||
|
||||
// Test if SendVirtualInput is sent exactly once with the suppress flag when Ctrl is remapped to Caps Lock
|
||||
TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirutalInputWithSuppressFlagExactlyOnce_WhenCtrlAltShiftIsMappedToCapsLock)
|
||||
TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenCtrlAltShiftIsMappedToCapsLock)
|
||||
{
|
||||
// Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) {
|
||||
@@ -153,10 +153,166 @@ namespace RemappingLogicTests
|
||||
input[0].ki.wVk = VK_CONTROL;
|
||||
|
||||
// Send Ctrl keydown
|
||||
mockedInputHandler.SendVirtualInput(1, input, sizeof(INPUT));
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly once with the above condition
|
||||
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
}
|
||||
|
||||
// Test if SendVirtualInput is sent exactly twice with the suppress flag when Caps Lock is remapped to shortcut with Ctrl and Shift
|
||||
TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyTwice_WhenCapsLockIsMappedToShortcutWithCtrlAltShift)
|
||||
{
|
||||
// Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remap Caps Lock to Ctrl+Shift+V
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_CONTROL);
|
||||
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;
|
||||
|
||||
// Send Caps Lock keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly twice with the above condition
|
||||
Assert::AreEqual(2, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
}
|
||||
|
||||
// Test if SendVirtualInput is sent exactly once with the suppress flag when Ctrl is remapped to a shortcut with Caps Lock
|
||||
TEST_METHOD (HandleSingleKeyRemapEvent_ShouldSendVirtualInputWithSuppressFlagExactlyOnce_WhenCtrlAltShiftIsMappedToShortcutWithCapsLock)
|
||||
{
|
||||
// Set sendvirtualinput call count condition to return true if the key event was sent with the suppress flag
|
||||
mockedInputHandler.SetSendVirtualInputTestHandler([](LowlevelKeyboardEvent* data) {
|
||||
if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remap Ctrl to Ctrl+Caps Lock
|
||||
Shortcut dest;
|
||||
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;
|
||||
|
||||
// Send Ctrl keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// SendVirtualInput should be called exactly once with the above condition
|
||||
Assert::AreEqual(1, mockedInputHandler.GetSendVirtualInputCallCount());
|
||||
}
|
||||
|
||||
// Test if correct keyboard states are set for a single key to two key shortcut remap
|
||||
TEST_METHOD (RemappedKeyToTwoKeyShortcut_ShouldSetTargetKeyState_OnKeyEvent)
|
||||
{
|
||||
// Remap A to Ctrl+V
|
||||
Shortcut dest;
|
||||
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;
|
||||
|
||||
// Send A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// 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;
|
||||
|
||||
// Send A keyup
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, and Ctrl, V key state should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
}
|
||||
|
||||
// Test if correct keyboard states are set for a single key to three key shortcut remap
|
||||
TEST_METHOD (RemappedKeyToThreeKeyShortcut_ShouldSetTargetKeyState_OnKeyEvent)
|
||||
{
|
||||
// Remap A to Ctrl+Shift+V
|
||||
Shortcut dest;
|
||||
dest.SetKey(VK_CONTROL);
|
||||
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;
|
||||
|
||||
// Send A keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// 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;
|
||||
|
||||
// Send A keyup
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// A key state should be unchanged, and Ctrl, Shift, V key state should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_SHIFT), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
}
|
||||
|
||||
// Test if correct keyboard states are set for a remap from a single key to a shortcut containing the source key
|
||||
TEST_METHOD (RemappedKeyToShortcutContainingSourceKey_ShouldSetTargetKeyState_OnKeyEvent)
|
||||
{
|
||||
// Remap LCtrl to LCtrl+V
|
||||
Shortcut dest;
|
||||
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;
|
||||
|
||||
// Send LCtrl keydown
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// 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;
|
||||
|
||||
// Send LCtrl keyup
|
||||
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||
|
||||
// LCtrl, V key state should be false
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_LCONTROL), false);
|
||||
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,44 @@
|
||||
#include "pch.h"
|
||||
#include "Dialog.h"
|
||||
#include <set>
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
|
||||
KeyboardManagerHelper::ErrorType Dialog::CheckIfRemappingsAreValid(const std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& remappings)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
||||
std::map<std::wstring, std::set<std::variant<DWORD, Shortcut>>> ogKeys;
|
||||
for (int i = 0; i < remappings.size(); i++)
|
||||
{
|
||||
std::variant<DWORD, Shortcut> ogKey = remappings[i].first[0];
|
||||
std::variant<DWORD, Shortcut> newKey = remappings[i].first[1];
|
||||
std::wstring appName = remappings[i].second;
|
||||
|
||||
bool ogKeyValidity = (ogKey.index() == 0 && std::get<DWORD>(ogKey) != NULL) || (ogKey.index() == 1 && std::get<Shortcut>(ogKey).IsValidShortcut());
|
||||
bool newKeyValidity = (newKey.index() == 0 && std::get<DWORD>(newKey) != NULL) || (newKey.index() == 1 && std::get<Shortcut>(newKey).IsValidShortcut());
|
||||
|
||||
// Add new set for a new target app name
|
||||
if (ogKeys.find(appName) == ogKeys.end())
|
||||
{
|
||||
ogKeys[appName] = std::set<std::variant<DWORD, Shortcut>>();
|
||||
}
|
||||
|
||||
if (ogKeyValidity && newKeyValidity && ogKeys[appName].find(ogKey) == ogKeys[appName].end())
|
||||
{
|
||||
ogKeys[appName].insert(ogKey);
|
||||
}
|
||||
else if (ogKeyValidity && newKeyValidity && ogKeys[appName].find(ogKey) != ogKeys[appName].end())
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
else
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
}
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
IAsyncOperation<bool> Dialog::PartialRemappingConfirmationDialog(XamlRoot root, std::wstring dialogTitle)
|
||||
{
|
||||
ContentDialog confirmationDialog;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include <set>
|
||||
#include <variant>
|
||||
|
||||
namespace winrt::Windows::UI::Xaml
|
||||
{
|
||||
@@ -19,33 +18,7 @@ namespace winrt::Windows::UI::Xaml
|
||||
|
||||
namespace Dialog
|
||||
{
|
||||
template<typename T>
|
||||
KeyboardManagerHelper::ErrorType CheckIfRemappingsAreValid(
|
||||
const std::vector<std::vector<T>>& remappings,
|
||||
std::function<bool(T)> isValid)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
||||
std::set<T> ogKeys;
|
||||
for (int i = 0; i < remappings.size(); i++)
|
||||
{
|
||||
T ogKey = remappings[i][0];
|
||||
T newKey = remappings[i][1];
|
||||
|
||||
if (isValid(ogKey) && isValid(newKey) && ogKeys.find(ogKey) == ogKeys.end())
|
||||
{
|
||||
ogKeys.insert(ogKey);
|
||||
}
|
||||
else if (isValid(ogKey) && isValid(newKey) && ogKeys.find(ogKey) != ogKeys.end())
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
else
|
||||
{
|
||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
}
|
||||
return isSuccess;
|
||||
}
|
||||
KeyboardManagerHelper::ErrorType CheckIfRemappingsAreValid(const std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& remappings);
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<bool> PartialRemappingConfirmationDialog(winrt::Windows::UI::Xaml::XamlRoot root, std::wstring dialogTitle);
|
||||
};
|
||||
|
||||
@@ -35,12 +35,18 @@ static std::vector<DWORD> GetOrphanedKeys()
|
||||
|
||||
for (int i = 0; i < SingleKeyRemapControl::singleKeyRemapBuffer.size(); i++)
|
||||
{
|
||||
DWORD ogKey = SingleKeyRemapControl::singleKeyRemapBuffer[i][0];
|
||||
DWORD newKey = SingleKeyRemapControl::singleKeyRemapBuffer[i][1];
|
||||
if (ogKey != 0 && newKey != 0)
|
||||
DWORD ogKey = std::get<DWORD>(SingleKeyRemapControl::singleKeyRemapBuffer[i].first[0]);
|
||||
std::variant<DWORD, Shortcut> newKey = SingleKeyRemapControl::singleKeyRemapBuffer[i].first[1];
|
||||
|
||||
if (ogKey != NULL && ((newKey.index() == 0 && std::get<DWORD>(newKey) != 0) || (newKey.index() == 1 && std::get<Shortcut>(newKey).IsValidShortcut())))
|
||||
{
|
||||
ogKeys.insert(ogKey);
|
||||
newKeys.insert(newKey);
|
||||
|
||||
// newKey should be added only if the target is a key
|
||||
if (SingleKeyRemapControl::singleKeyRemapBuffer[i].first[1].index() == 0)
|
||||
{
|
||||
newKeys.insert(std::get<DWORD>(newKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,11 +92,8 @@ static IAsyncOperation<bool> OrphanKeysConfirmationDialog(
|
||||
|
||||
static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, XamlRoot root, std::function<void()> ApplyRemappings)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType isSuccess = Dialog::CheckIfRemappingsAreValid<DWORD>(
|
||||
SingleKeyRemapControl::singleKeyRemapBuffer,
|
||||
[](DWORD key) {
|
||||
return key != 0;
|
||||
});
|
||||
KeyboardManagerHelper::ErrorType isSuccess = Dialog::CheckIfRemappingsAreValid(SingleKeyRemapControl::singleKeyRemapBuffer);
|
||||
|
||||
if (isSuccess != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
if (!co_await Dialog::PartialRemappingConfirmationDialog(root, L"Some of the keys could not be remapped. Do you want to continue anyway?"))
|
||||
@@ -98,6 +101,7 @@ static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, Xa
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for orphaned keys
|
||||
// Draw content Dialog
|
||||
std::vector<DWORD> orphanedKeys = GetOrphanedKeys();
|
||||
@@ -112,7 +116,7 @@ static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, Xa
|
||||
}
|
||||
|
||||
// Function to combine remappings if the L and R version of the modifier is mapped to the same key
|
||||
void CombineRemappings(std::unordered_map<DWORD, DWORD>& table, DWORD leftKey, DWORD rightKey, DWORD combinedKey)
|
||||
void CombineRemappings(std::unordered_map<DWORD, std::variant<DWORD, Shortcut>>& table, DWORD leftKey, DWORD rightKey, DWORD combinedKey)
|
||||
{
|
||||
if (table.find(leftKey) != table.end() && table.find(rightKey) != table.end())
|
||||
{
|
||||
@@ -127,7 +131,7 @@ void CombineRemappings(std::unordered_map<DWORD, DWORD>& table, DWORD leftKey, D
|
||||
}
|
||||
|
||||
// Function to pre process the remap table before loading it into the UI
|
||||
void PreProcessRemapTable(std::unordered_map<DWORD, DWORD>& table)
|
||||
void PreProcessRemapTable(std::unordered_map<DWORD, std::variant<DWORD, Shortcut>>& table)
|
||||
{
|
||||
// Pre process the table to combine L and R versions of Ctrl/Alt/Shift/Win that are mapped to the same key
|
||||
CombineRemappings(table, VK_LCONTROL, VK_RCONTROL, VK_CONTROL);
|
||||
@@ -234,13 +238,13 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
|
||||
// Text block for information about remap key section.
|
||||
TextBlock keyRemapInfoHeader;
|
||||
keyRemapInfoHeader.Text(L"Select the key you want to change (Key) and the key you want it to become (Mapped To).");
|
||||
keyRemapInfoHeader.Text(L"Select the key you want to change (Key) and then the key or shortcut you want it to become (Mapped To).");
|
||||
keyRemapInfoHeader.Margin({ 10, 0, 0, 10 });
|
||||
keyRemapInfoHeader.FontWeight(Text::FontWeights::SemiBold());
|
||||
keyRemapInfoHeader.TextWrapping(TextWrapping::Wrap);
|
||||
|
||||
TextBlock keyRemapInfoExample;
|
||||
keyRemapInfoExample.Text(L"For example, if you want to press A and get B, Key A would be your \"Key\" and Key B would be your \"Mapped To\".");
|
||||
keyRemapInfoExample.Text(L"For example, if you want to press A and get \"Ctrl+C\", key \"A\" would be your \"Key\" column and the shortcut \"Ctrl+C\" would be your \"Mapped To\" column.");
|
||||
keyRemapInfoExample.Margin({ 10, 0, 0, 20 });
|
||||
keyRemapInfoExample.FontStyle(Text::FontStyle::Italic);
|
||||
keyRemapInfoExample.TextWrapping(TextWrapping::Wrap);
|
||||
@@ -253,8 +257,8 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
ColumnDefinition arrowColumn;
|
||||
arrowColumn.MinWidth(KeyboardManagerConstants::TableArrowColWidth);
|
||||
ColumnDefinition newColumn;
|
||||
newColumn.MinWidth(KeyboardManagerConstants::RemapTableDropDownWidth);
|
||||
newColumn.MaxWidth(KeyboardManagerConstants::RemapTableDropDownWidth);
|
||||
newColumn.MinWidth(3 * KeyboardManagerConstants::ShortcutTableDropDownWidth + 2 * KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
newColumn.MaxWidth(3 * KeyboardManagerConstants::ShortcutTableDropDownWidth + 2 * KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
ColumnDefinition removeColumn;
|
||||
removeColumn.MinWidth(KeyboardManagerConstants::TableRemoveColWidth);
|
||||
keyRemapTable.Margin({ 10, 10, 10, 20 });
|
||||
@@ -300,7 +304,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
|
||||
// Load existing remaps into UI
|
||||
std::unique_lock<std::mutex> lock(keyboardManagerState.singleKeyReMap_mutex);
|
||||
std::unordered_map<DWORD, DWORD> singleKeyRemapCopy = keyboardManagerState.singleKeyReMap;
|
||||
std::unordered_map<DWORD, std::variant<DWORD, Shortcut>> singleKeyRemapCopy = keyboardManagerState.singleKeyReMap;
|
||||
lock.unlock();
|
||||
PreProcessRemapTable(singleKeyRemapCopy);
|
||||
|
||||
@@ -322,13 +326,14 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
||||
// Clear existing Key Remaps
|
||||
keyboardManagerState.ClearSingleKeyRemaps();
|
||||
DWORD successfulRemapCount = 0;
|
||||
DWORD successfulKeyToKeyRemapCount = 0;
|
||||
DWORD successfulKeyToShortcutRemapCount = 0;
|
||||
for (int i = 0; i < SingleKeyRemapControl::singleKeyRemapBuffer.size(); i++)
|
||||
{
|
||||
DWORD originalKey = SingleKeyRemapControl::singleKeyRemapBuffer[i][0];
|
||||
DWORD newKey = SingleKeyRemapControl::singleKeyRemapBuffer[i][1];
|
||||
DWORD originalKey = std::get<DWORD>(SingleKeyRemapControl::singleKeyRemapBuffer[i].first[0]);
|
||||
std::variant<DWORD, Shortcut> newKey = SingleKeyRemapControl::singleKeyRemapBuffer[i].first[1];
|
||||
|
||||
if (originalKey != NULL && newKey != NULL)
|
||||
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !std::get<Shortcut>(newKey).IsValidShortcut()))
|
||||
{
|
||||
// If Ctrl/Alt/Shift are added, add their L and R versions instead to the same key
|
||||
bool result = false;
|
||||
@@ -366,7 +371,14 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulRemapCount += 1;
|
||||
if (newKey.index() == 0)
|
||||
{
|
||||
successfulKeyToKeyRemapCount += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulKeyToShortcutRemapCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -375,7 +387,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
||||
}
|
||||
}
|
||||
|
||||
Trace::KeyRemapCount(successfulRemapCount);
|
||||
Trace::KeyRemapCount(successfulKeyToKeyRemapCount, successfulKeyToShortcutRemapCount);
|
||||
// Save the updated shortcuts remaps to file.
|
||||
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
||||
if (!saveResult)
|
||||
|
||||
@@ -31,36 +31,7 @@ static IAsyncAction OnClickAccept(
|
||||
XamlRoot root,
|
||||
std::function<void()> ApplyRemappings)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
|
||||
std::map<std::wstring, std::vector<std::vector<Shortcut>>> appSpecificBuffer;
|
||||
|
||||
// Create per app shortcut buffer
|
||||
for (int i = 0; i < ShortcutControl::shortcutRemapBuffer.size(); i++)
|
||||
{
|
||||
std::wstring currAppName = ShortcutControl::shortcutRemapBuffer[i].second;
|
||||
std::transform(currAppName.begin(), currAppName.end(), currAppName.begin(), towlower);
|
||||
|
||||
if (appSpecificBuffer.find(currAppName) == appSpecificBuffer.end())
|
||||
{
|
||||
appSpecificBuffer[currAppName] = std::vector<std::vector<Shortcut>>();
|
||||
}
|
||||
|
||||
appSpecificBuffer[currAppName].push_back(ShortcutControl::shortcutRemapBuffer[i].first);
|
||||
}
|
||||
|
||||
for (auto it : appSpecificBuffer)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType currentSuccess = Dialog::CheckIfRemappingsAreValid<Shortcut>(
|
||||
it.second,
|
||||
[](Shortcut shortcut) {
|
||||
return shortcut.IsValidShortcut();
|
||||
});
|
||||
if (currentSuccess != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
isSuccess = currentSuccess;
|
||||
break;
|
||||
}
|
||||
}
|
||||
KeyboardManagerHelper::ErrorType isSuccess = Dialog::CheckIfRemappingsAreValid(ShortcutControl::shortcutRemapBuffer);
|
||||
|
||||
if (isSuccess != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
@@ -171,13 +142,13 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
|
||||
// Text block for information about remap key section.
|
||||
TextBlock shortcutRemapInfoHeader;
|
||||
shortcutRemapInfoHeader.Text(L"Select shortcut you want to change (Shortcut) and the shortcut you want it to invoke (Mapped To).");
|
||||
shortcutRemapInfoHeader.Text(L"Select the shortcut you want to change (Shortcut) and then the key or shortcut you want it to invoke (Mapped To).");
|
||||
shortcutRemapInfoHeader.Margin({ 10, 0, 0, 10 });
|
||||
shortcutRemapInfoHeader.FontWeight(Text::FontWeights::SemiBold());
|
||||
shortcutRemapInfoHeader.TextWrapping(TextWrapping::Wrap);
|
||||
|
||||
TextBlock shortcutRemapInfoExample;
|
||||
shortcutRemapInfoExample.Text(L"For example, if you want Ctrl+C to paste, Ctrl+C is the \"Shortcut\" and Ctrl+V would be your \"Mapped To\".");
|
||||
shortcutRemapInfoExample.Text(L"For example, if you want to press \"Ctrl+C\" and get \"Alt\" only on Microsoft Edge, \"Ctrl+C\" would be your \"Shortcut\" column, the key \"Alt\" would be your \"Mapped To\" column, and \"MSEdge\" would be your \"Target App\" column. If no target app is entered, it will apply globally. The name must be the process name and not the app name.");
|
||||
shortcutRemapInfoExample.Margin({ 10, 0, 0, 20 });
|
||||
shortcutRemapInfoExample.FontStyle(Text::FontStyle::Italic);
|
||||
shortcutRemapInfoExample.TextWrapping(TextWrapping::Wrap);
|
||||
@@ -285,15 +256,17 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
// Clear existing shortcuts
|
||||
keyboardManagerState.ClearOSLevelShortcuts();
|
||||
keyboardManagerState.ClearAppSpecificShortcuts();
|
||||
DWORD successfulOSLevelRemapCount = 0;
|
||||
DWORD successfulAppSpecificRemapCount = 0;
|
||||
DWORD successfulOSLevelShortcutToShortcutRemapCount = 0;
|
||||
DWORD successfulOSLevelShortcutToKeyRemapCount = 0;
|
||||
DWORD successfulAppSpecificShortcutToShortcutRemapCount = 0;
|
||||
DWORD successfulAppSpecificShortcutToKeyRemapCount = 0;
|
||||
// Save the shortcuts that are valid and report if any of them were invalid
|
||||
for (int i = 0; i < ShortcutControl::shortcutRemapBuffer.size(); i++)
|
||||
{
|
||||
Shortcut originalShortcut = ShortcutControl::shortcutRemapBuffer[i].first[0];
|
||||
Shortcut newShortcut = ShortcutControl::shortcutRemapBuffer[i].first[1];
|
||||
Shortcut originalShortcut = std::get<Shortcut>(ShortcutControl::shortcutRemapBuffer[i].first[0]);
|
||||
std::variant<DWORD, Shortcut> newShortcut = ShortcutControl::shortcutRemapBuffer[i].first[1];
|
||||
|
||||
if (originalShortcut.IsValidShortcut() && newShortcut.IsValidShortcut())
|
||||
if (originalShortcut.IsValidShortcut() && ((newShortcut.index() == 0 && std::get<DWORD>(newShortcut) != NULL) || (newShortcut.index() == 1 && std::get<Shortcut>(newShortcut).IsValidShortcut())))
|
||||
{
|
||||
if (ShortcutControl::shortcutRemapBuffer[i].second == L"")
|
||||
{
|
||||
@@ -304,7 +277,14 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulOSLevelRemapCount += 1;
|
||||
if (newShortcut.index() == 0)
|
||||
{
|
||||
successfulOSLevelShortcutToKeyRemapCount += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulOSLevelShortcutToShortcutRemapCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -316,7 +296,14 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulAppSpecificRemapCount += 1;
|
||||
if (newShortcut.index() == 0)
|
||||
{
|
||||
successfulAppSpecificShortcutToKeyRemapCount += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulAppSpecificShortcutToShortcutRemapCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,8 +314,8 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
||||
}
|
||||
|
||||
// Telemetry events
|
||||
Trace::OSLevelShortcutRemapCount(successfulOSLevelRemapCount);
|
||||
Trace::AppSpecificShortcutRemapCount(successfulAppSpecificRemapCount);
|
||||
Trace::OSLevelShortcutRemapCount(successfulOSLevelShortcutToShortcutRemapCount, successfulOSLevelShortcutToKeyRemapCount);
|
||||
Trace::AppSpecificShortcutRemapCount(successfulAppSpecificShortcutToShortcutRemapCount, successfulAppSpecificShortcutToKeyRemapCount);
|
||||
|
||||
// Save the updated key remaps to file.
|
||||
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
||||
|
||||
@@ -53,7 +53,7 @@ void KeyDropDownControl::CheckAndUpdateKeyboardLayout(ComboBox currentDropDown,
|
||||
}
|
||||
|
||||
// Function to set selection handler for single key remap drop down. Needs to be called after the constructor since the singleKeyControl StackPanel is null if called in the constructor
|
||||
void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel singleKeyControl, int colIndex, std::vector<std::vector<DWORD>>& singleKeyRemapBuffer)
|
||||
void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel singleKeyControl, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& singleKeyRemapBuffer)
|
||||
{
|
||||
// drop down selection handler
|
||||
auto onSelectionChange = [&, table, singleKeyControl, colIndex](winrt::Windows::Foundation::IInspectable const& sender) {
|
||||
@@ -70,10 +70,15 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel singleKeyCo
|
||||
if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex)
|
||||
{
|
||||
// Check if the value being set is the same as the other column
|
||||
if (singleKeyRemapBuffer[rowIndex][std::abs(int(colIndex) - 1)] == keyCodeList[selectedKeyIndex])
|
||||
if (singleKeyRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 0)
|
||||
{
|
||||
if (std::get<DWORD>(singleKeyRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == keyCodeList[selectedKeyIndex])
|
||||
{
|
||||
errorType = KeyboardManagerHelper::ErrorType::MapToSameKey;
|
||||
}
|
||||
}
|
||||
|
||||
// If one column is shortcut and other is key no warning required
|
||||
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError && colIndex == 0)
|
||||
{
|
||||
@@ -82,30 +87,37 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel singleKeyCo
|
||||
{
|
||||
if (i != rowIndex)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType result = KeyboardManagerHelper::DoKeysOverlap(singleKeyRemapBuffer[i][colIndex], keyCodeList[selectedKeyIndex]);
|
||||
if (singleKeyRemapBuffer[i].first[colIndex].index() == 0)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType result = KeyboardManagerHelper::DoKeysOverlap(std::get<DWORD>(singleKeyRemapBuffer[i].first[colIndex]), keyCodeList[selectedKeyIndex]);
|
||||
if (result != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
errorType = result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// check key to shortcut error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no error, set the buffer
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
singleKeyRemapBuffer[rowIndex][colIndex] = keyCodeList[selectedKeyIndex];
|
||||
singleKeyRemapBuffer[rowIndex].first[colIndex] = keyCodeList[selectedKeyIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
singleKeyRemapBuffer[rowIndex][colIndex] = NULL;
|
||||
singleKeyRemapBuffer[rowIndex].first[colIndex] = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset to null if the key is not found
|
||||
singleKeyRemapBuffer[rowIndex][colIndex] = NULL;
|
||||
singleKeyRemapBuffer[rowIndex].first[colIndex] = NULL;
|
||||
}
|
||||
|
||||
if (errorType != KeyboardManagerHelper::ErrorType::NoError)
|
||||
@@ -130,7 +142,7 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel singleKeyCo
|
||||
});
|
||||
}
|
||||
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateShortcutSelection(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp)
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateShortcutSelection(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
{
|
||||
ComboBox currentDropDown = dropDown.as<ComboBox>();
|
||||
int selectedKeyIndex = currentDropDown.SelectedIndex();
|
||||
@@ -144,12 +156,19 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
int rowIndex = -1;
|
||||
|
||||
if (controlIindexFound)
|
||||
{
|
||||
if (isSingleKeyWindow)
|
||||
{
|
||||
rowIndex = (controlIndex - KeyboardManagerConstants::RemapTableHeaderCount) / KeyboardManagerConstants::RemapTableColCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
rowIndex = (controlIndex - KeyboardManagerConstants::ShortcutTableHeaderCount) / KeyboardManagerConstants::ShortcutTableColCount;
|
||||
}
|
||||
if (selectedKeyIndex != -1 && keyCodeList.size() > selectedKeyIndex && dropDownFound)
|
||||
{
|
||||
// If only 1 drop down and action key is chosen: Warn that a modifier must be chosen
|
||||
if (parent.Children().Size() == 1 && !KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]))
|
||||
// If only 1 drop down and action key is chosen: Warn that a modifier must be chosen (if the drop down is not for a hybrid scenario)
|
||||
if (parent.Children().Size() == 1 && !KeyboardManagerHelper::IsModifierKey(keyCodeList[selectedKeyIndex]) && !isHybridControl)
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
|
||||
@@ -169,7 +188,7 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
// If not, add a new drop down
|
||||
else
|
||||
{
|
||||
AddDropDown(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp);
|
||||
AddDropDown(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
|
||||
}
|
||||
}
|
||||
// If last drop down and a modifier is selected but there are already max drop downs: warn the user
|
||||
@@ -180,10 +199,20 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
}
|
||||
// If None is selected but it's the last index: warn
|
||||
else if (keyCodeList[selectedKeyIndex] == 0)
|
||||
{
|
||||
// If it is a hybrid control and there are 2 drop downs then deletion is allowed
|
||||
if (isHybridControl && parent.Children().Size() == KeyboardManagerConstants::MinShortcutSize)
|
||||
{
|
||||
// set delete drop down flag
|
||||
IsDeleteDropDownRequired = true;
|
||||
// do not delete the drop down now since there may be some other error which would cause the drop down to be invalid after removal
|
||||
}
|
||||
else
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutOneActionKey;
|
||||
}
|
||||
}
|
||||
// If none of the above, then the action key will be set
|
||||
}
|
||||
// If it is the not the last drop down
|
||||
@@ -207,12 +236,22 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
// do not delete the drop down now since there may be some other error which would cause the drop down to be invalid after removal
|
||||
}
|
||||
else if (keyCodeList[selectedKeyIndex] == 0 && parent.Children().Size() <= KeyboardManagerConstants::MinShortcutSize)
|
||||
{
|
||||
// If it is a hybrid control and there are 2 drop downs then deletion is allowed
|
||||
if (isHybridControl && parent.Children().Size() == KeyboardManagerConstants::MinShortcutSize)
|
||||
{
|
||||
// set delete drop down flag
|
||||
IsDeleteDropDownRequired = true;
|
||||
// do not delete the drop down now since there may be some other error which would cause the drop down to be invalid after removal
|
||||
}
|
||||
else
|
||||
{
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutAtleast2Keys;
|
||||
}
|
||||
// If the user tries to set an action key check if all drop down menus after this are empty if it is not the first key
|
||||
else if (dropDownIndex != 0)
|
||||
}
|
||||
// If the user tries to set an action key check if all drop down menus after this are empty if it is not the first key. If it is a hybrid control, this can be done even on the first key
|
||||
else if (dropDownIndex != 0 || isHybridControl)
|
||||
{
|
||||
bool isClear = true;
|
||||
for (int i = dropDownIndex + 1; i < (int)parent.Children().Size(); i++)
|
||||
@@ -254,9 +293,22 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
// After validating the shortcut, now for errors like remap to same shortcut, remap shortcut more than once, Win L and Ctrl Alt Del
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
Shortcut tempShortcut;
|
||||
tempShortcut.SetKeyCodes(GetKeysFromStackPanel(parent));
|
||||
std::wstring appName = targetApp.Text().c_str();
|
||||
std::variant<DWORD, Shortcut> tempShortcut;
|
||||
std::vector<DWORD> selectedKeyCodes = GetKeysFromStackPanel(parent);
|
||||
if (isHybridControl && selectedKeyCodes.size() == 1)
|
||||
{
|
||||
tempShortcut = selectedKeyCodes[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
tempShortcut = Shortcut();
|
||||
std::get<Shortcut>(tempShortcut).SetKeyCodes(GetKeysFromStackPanel(parent));
|
||||
}
|
||||
std::wstring appName;
|
||||
if (targetApp != nullptr)
|
||||
{
|
||||
appName = targetApp.Text().c_str();
|
||||
}
|
||||
// Convert app name to lower case
|
||||
std::transform(appName.begin(), appName.end(), appName.begin(), towlower);
|
||||
std::wstring lowercaseDefAppName = KeyboardManagerConstants::DefaultAppName;
|
||||
@@ -266,11 +318,33 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
appName = L"";
|
||||
}
|
||||
|
||||
// Check if the value being set is the same as the other column
|
||||
if (shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)] == tempShortcut && shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].IsValidShortcut() && tempShortcut.IsValidShortcut())
|
||||
// Check if the value being set is the same as the other column - index of other column does not have to be checked since only one column is hybrid
|
||||
if (tempShortcut.index() == 1)
|
||||
{
|
||||
// If shortcut to shortcut
|
||||
if (shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 1)
|
||||
{
|
||||
if (std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == std::get<Shortcut>(tempShortcut) && std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]).IsValidShortcut() && std::get<Shortcut>(tempShortcut).IsValidShortcut())
|
||||
{
|
||||
errorType = KeyboardManagerHelper::ErrorType::MapToSameShortcut;
|
||||
}
|
||||
}
|
||||
|
||||
// If one column is shortcut and other is key no warning required
|
||||
}
|
||||
else
|
||||
{
|
||||
// If key to key
|
||||
if (shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 0)
|
||||
{
|
||||
if (std::get<DWORD>(shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == std::get<DWORD>(tempShortcut) && std::get<DWORD>(shortcutRemapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) != NULL && std::get<DWORD>(tempShortcut) != NULL)
|
||||
{
|
||||
errorType = KeyboardManagerHelper::ErrorType::MapToSameKey;
|
||||
}
|
||||
}
|
||||
|
||||
// If one column is shortcut and other is key no warning required
|
||||
}
|
||||
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError && colIndex == 0)
|
||||
{
|
||||
@@ -282,7 +356,29 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
|
||||
if (i != rowIndex && currAppName == appName)
|
||||
{
|
||||
KeyboardManagerHelper::ErrorType result = Shortcut::DoKeysOverlap(shortcutRemapBuffer[i].first[colIndex], tempShortcut);
|
||||
KeyboardManagerHelper::ErrorType result = KeyboardManagerHelper::ErrorType::NoError;
|
||||
if (!isHybridControl)
|
||||
{
|
||||
result = Shortcut::DoKeysOverlap(std::get<Shortcut>(shortcutRemapBuffer[i].first[colIndex]), std::get<Shortcut>(tempShortcut));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tempShortcut.index() == 0 && shortcutRemapBuffer[i].first[colIndex].index() == 0)
|
||||
{
|
||||
if (std::get<DWORD>(tempShortcut) != NULL && std::get<DWORD>(shortcutRemapBuffer[i].first[colIndex]) != NULL)
|
||||
{
|
||||
result = KeyboardManagerHelper::DoKeysOverlap(std::get<DWORD>(shortcutRemapBuffer[i].first[colIndex]), std::get<DWORD>(tempShortcut));
|
||||
}
|
||||
}
|
||||
else if (tempShortcut.index() == 1 && shortcutRemapBuffer[i].first[colIndex].index() == 1)
|
||||
{
|
||||
if (std::get<Shortcut>(tempShortcut).IsValidShortcut() && std::get<Shortcut>(shortcutRemapBuffer[i].first[colIndex]).IsValidShortcut())
|
||||
{
|
||||
result = Shortcut::DoKeysOverlap(std::get<Shortcut>(shortcutRemapBuffer[i].first[colIndex]), std::get<Shortcut>(tempShortcut));
|
||||
}
|
||||
}
|
||||
// Other scenarios not possible since key to shortcut is with key to key, and shortcut to key is with shortcut to shortcut
|
||||
}
|
||||
if (result != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
errorType = result;
|
||||
@@ -292,9 +388,9 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
}
|
||||
}
|
||||
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError)
|
||||
if (errorType == KeyboardManagerHelper::ErrorType::NoError && tempShortcut.index() == 1)
|
||||
{
|
||||
errorType = tempShortcut.IsShortcutIllegal();
|
||||
errorType = std::get<Shortcut>(tempShortcut).IsShortcutIllegal();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,10 +413,10 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
||||
}
|
||||
|
||||
// Function to set selection handler for shortcut drop down. Needs to be called after the constructor since the shortcutControl StackPanel is null if called in the constructor
|
||||
void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox& targetApp)
|
||||
void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox& targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
{
|
||||
auto onSelectionChange = [&, table, shortcutControl, colIndex, parent, targetApp](winrt::Windows::Foundation::IInspectable const& sender) {
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> validationResult = ValidateShortcutSelection(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp);
|
||||
auto onSelectionChange = [&, table, shortcutControl, colIndex, parent, targetApp, isHybridControl, isSingleKeyWindow](winrt::Windows::Foundation::IInspectable const& sender) {
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> validationResult = ValidateShortcutSelection(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
|
||||
|
||||
// Check if the drop down row index was identified from the return value of validateSelection
|
||||
if (validationResult.second != -1)
|
||||
@@ -329,11 +425,32 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel shortcutCon
|
||||
if (validationResult.first != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
// Validate all the drop downs
|
||||
ValidateShortcutFromDropDownList(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp);
|
||||
ValidateShortcutFromDropDownList(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
|
||||
}
|
||||
|
||||
// Reset the buffer based on the new selected drop down items
|
||||
shortcutRemapBuffer[validationResult.second].first[colIndex].SetKeyCodes(GetKeysFromStackPanel(parent));
|
||||
std::vector selectedKeyCodes = GetKeysFromStackPanel(parent);
|
||||
if (!isHybridControl)
|
||||
{
|
||||
std::get<Shortcut>(shortcutRemapBuffer[validationResult.second].first[colIndex]).SetKeyCodes(selectedKeyCodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If exactly one key is selected consider it to be a key remap
|
||||
if (selectedKeyCodes.size() == 1)
|
||||
{
|
||||
shortcutRemapBuffer[validationResult.second].first[colIndex] = selectedKeyCodes[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
Shortcut tempShortcut;
|
||||
tempShortcut.SetKeyCodes(selectedKeyCodes);
|
||||
// Assign instead of setting the value in the buffer since the previous value may not be a Shortcut
|
||||
shortcutRemapBuffer[validationResult.second].first[colIndex] = tempShortcut;
|
||||
}
|
||||
}
|
||||
if (targetApp != nullptr)
|
||||
{
|
||||
std::wstring newText = targetApp.Text().c_str();
|
||||
std::wstring lowercaseDefAppName = KeyboardManagerConstants::DefaultAppName;
|
||||
std::transform(newText.begin(), newText.end(), newText.begin(), towlower);
|
||||
@@ -347,6 +464,7 @@ void KeyDropDownControl::SetSelectionHandler(Grid& table, StackPanel shortcutCon
|
||||
shortcutRemapBuffer[validationResult.second].second = targetApp.Text().c_str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the user searches for a key the selection handler gets invoked however if they click away it reverts back to the previous state. This can result in dangling references to added drop downs which were then reset.
|
||||
// We handle this by removing the drop down if it no longer a child of the parent
|
||||
@@ -389,11 +507,11 @@ ComboBox KeyDropDownControl::GetComboBox()
|
||||
}
|
||||
|
||||
// Function to add a drop down to the shortcut stack panel
|
||||
void KeyDropDownControl::AddDropDown(Grid table, StackPanel shortcutControl, StackPanel parent, const int colIndex, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox& targetApp)
|
||||
void KeyDropDownControl::AddDropDown(Grid table, StackPanel shortcutControl, StackPanel parent, const int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
{
|
||||
keyDropDownControlObjects.push_back(std::move(std::unique_ptr<KeyDropDownControl>(new KeyDropDownControl(true))));
|
||||
parent.Children().Append(keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->GetComboBox());
|
||||
keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->SetSelectionHandler(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp);
|
||||
keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->SetSelectionHandler(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
|
||||
parent.UpdateLayout();
|
||||
}
|
||||
|
||||
@@ -455,19 +573,29 @@ bool KeyDropDownControl::CheckRepeatedModifier(StackPanel parent, int selectedKe
|
||||
}
|
||||
|
||||
// Function for validating the selection of shortcuts for all the associated drop downs
|
||||
void KeyDropDownControl::ValidateShortcutFromDropDownList(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp)
|
||||
void KeyDropDownControl::ValidateShortcutFromDropDownList(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
{
|
||||
// Iterate over all drop downs from left to right in that row/col and validate if there is an error in any of the drop downs. After this the state should be error-free (if it is a valid shortcut)
|
||||
for (int i = 0; i < keyDropDownControlObjects.size(); i++)
|
||||
{
|
||||
// Check for errors only if the current selection is a valid shortcut
|
||||
Shortcut tempComputedShortcut;
|
||||
tempComputedShortcut.SetKeyCodes(keyDropDownControlObjects[i]->GetKeysFromStackPanel(parent));
|
||||
|
||||
// If the shortcut is valid and that drop down is not empty
|
||||
if (tempComputedShortcut.IsValidShortcut() && keyDropDownControlObjects[i]->GetComboBox().SelectedIndex() != -1)
|
||||
std::vector<DWORD> selectedKeyCodes = keyDropDownControlObjects[i]->GetKeysFromStackPanel(parent);
|
||||
std::variant<DWORD, Shortcut> currentShortcut;
|
||||
if (selectedKeyCodes.size() == 1 && isHybridControl)
|
||||
{
|
||||
keyDropDownControlObjects[i]->ValidateShortcutSelection(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp);
|
||||
currentShortcut = selectedKeyCodes[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
Shortcut temp;
|
||||
temp.SetKeyCodes(selectedKeyCodes);
|
||||
currentShortcut = temp;
|
||||
}
|
||||
|
||||
// If the key/shortcut is valid and that drop down is not empty
|
||||
if (((currentShortcut.index() == 0 && std::get<DWORD>(currentShortcut) != NULL) || (currentShortcut.index() == 1 && std::get<Shortcut>(currentShortcut).IsValidShortcut())) && keyDropDownControlObjects[i]->GetComboBox().SelectedIndex() != -1)
|
||||
{
|
||||
keyDropDownControlObjects[i]->ValidateShortcutSelection(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -479,3 +607,33 @@ void KeyDropDownControl::SetDropDownError(ComboBox currentDropDown, hstring mess
|
||||
warningMessage.as<TextBlock>().Text(message);
|
||||
currentDropDown.ContextFlyout().ShowAttachedFlyout((FrameworkElement)dropDown.as<ComboBox>());
|
||||
}
|
||||
|
||||
// Function to add a shortcut to the UI control as combo boxes
|
||||
void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& remapBuffer, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
|
||||
{
|
||||
// Delete the existing drop down menus
|
||||
parent.Children().Clear();
|
||||
// Remove references to the old drop down objects to destroy them
|
||||
keyDropDownControlObjects.clear();
|
||||
|
||||
std::vector<DWORD> shortcutKeyCodes = shortcut.GetKeyCodes();
|
||||
std::vector<DWORD> keyCodeList = keyboardManagerState.keyboardMap.GetKeyCodeList(true);
|
||||
if (shortcutKeyCodes.size() != 0)
|
||||
{
|
||||
KeyDropDownControl::AddDropDown(table, controlLayout, parent, colIndex, remapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
|
||||
for (int i = 0; i < shortcutKeyCodes.size(); i++)
|
||||
{
|
||||
// New drop down gets added automatically when the SelectedIndex is set
|
||||
if (i < (int)parent.Children().Size())
|
||||
{
|
||||
ComboBox currentDropDown = parent.Children().GetAt(i).as<ComboBox>();
|
||||
auto it = std::find(keyCodeList.begin(), keyCodeList.end(), shortcutKeyCodes[i]);
|
||||
if (it != keyCodeList.end())
|
||||
{
|
||||
currentDropDown.SelectedIndex((int32_t)std::distance(keyCodeList.begin(), it));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
parent.UpdateLayout();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <keyboardmanager/common/Shortcut.h>
|
||||
#include <variant>
|
||||
class KeyboardManagerState;
|
||||
class Shortcut;
|
||||
|
||||
namespace winrt::Windows
|
||||
{
|
||||
@@ -56,13 +57,13 @@ public:
|
||||
}
|
||||
|
||||
// Function to set selection handler for single key remap drop down. Needs to be called after the constructor since the singleKeyControl StackPanel is null if called in the constructor
|
||||
void SetSelectionHandler(winrt::Windows::UI::Xaml::Controls::Grid& table, winrt::Windows::UI::Xaml::Controls::StackPanel singleKeyControl, int colIndex, std::vector<std::vector<DWORD>>& singleKeyRemapBuffer);
|
||||
void SetSelectionHandler(winrt::Windows::UI::Xaml::Controls::Grid& table, winrt::Windows::UI::Xaml::Controls::StackPanel singleKeyControl, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& singleKeyRemapBuffer);
|
||||
|
||||
// Function for validating the selection of shortcuts for the drop down
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> ValidateShortcutSelection(winrt::Windows::UI::Xaml::Controls::Grid table, winrt::Windows::UI::Xaml::Controls::StackPanel shortcutControl, winrt::Windows::UI::Xaml::Controls::StackPanel parent, int colIndex, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox targetApp);
|
||||
std::pair<KeyboardManagerHelper::ErrorType, int> ValidateShortcutSelection(winrt::Windows::UI::Xaml::Controls::Grid table, winrt::Windows::UI::Xaml::Controls::StackPanel shortcutControl, winrt::Windows::UI::Xaml::Controls::StackPanel parent, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
|
||||
// Function to set selection handler for shortcut drop down. Needs to be called after the constructor since the shortcutControl StackPanel is null if called in the constructor
|
||||
void SetSelectionHandler(winrt::Windows::UI::Xaml::Controls::Grid& table, winrt::Windows::UI::Xaml::Controls::StackPanel shortcutControl, winrt::Windows::UI::Xaml::Controls::StackPanel parent, int colIndex, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox& targetApp);
|
||||
void SetSelectionHandler(winrt::Windows::UI::Xaml::Controls::Grid& table, winrt::Windows::UI::Xaml::Controls::StackPanel shortcutControl, winrt::Windows::UI::Xaml::Controls::StackPanel parent, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox& targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
|
||||
// Function to set the selected index of the drop down
|
||||
void SetSelectedIndex(int32_t index);
|
||||
@@ -71,7 +72,7 @@ public:
|
||||
ComboBox GetComboBox();
|
||||
|
||||
// Function to add a drop down to the shortcut stack panel
|
||||
static void AddDropDown(winrt::Windows::UI::Xaml::Controls::Grid table, winrt::Windows::UI::Xaml::Controls::StackPanel shortcutControl, winrt::Windows::UI::Xaml::Controls::StackPanel parent, const int colIndex, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox& targetApp);
|
||||
static void AddDropDown(winrt::Windows::UI::Xaml::Controls::Grid table, winrt::Windows::UI::Xaml::Controls::StackPanel shortcutControl, winrt::Windows::UI::Xaml::Controls::StackPanel parent, const int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
|
||||
// Function to get the list of key codes from the shortcut combo box stack panel
|
||||
static std::vector<DWORD> GetKeysFromStackPanel(StackPanel parent);
|
||||
@@ -80,8 +81,11 @@ public:
|
||||
static bool CheckRepeatedModifier(winrt::Windows::UI::Xaml::Controls::StackPanel parent, int selectedKeyIndex, const std::vector<DWORD>& keyCodeList);
|
||||
|
||||
// Function for validating the selection of shortcuts for all the associated drop downs
|
||||
static void ValidateShortcutFromDropDownList(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp);
|
||||
static void ValidateShortcutFromDropDownList(Grid table, StackPanel shortcutControl, StackPanel parent, int colIndex, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
|
||||
// Function to set the warning message
|
||||
void SetDropDownError(winrt::Windows::UI::Xaml::Controls::ComboBox currentDropDown, winrt::hstring message);
|
||||
|
||||
// Function to add a shortcut to the UI control as combo boxes
|
||||
static void AddShortcutToControl(Shortcut shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& remapBuffer, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
|
||||
};
|
||||
|
||||
@@ -2,28 +2,30 @@
|
||||
#include "ShortcutControl.h"
|
||||
#include "KeyDropDownControl.h"
|
||||
#include "keyboardmanager/common/KeyboardManagerState.h"
|
||||
#include "keyboardmanager/common/Helpers.h"
|
||||
|
||||
//Both static members are initialized to null
|
||||
HWND ShortcutControl::EditShortcutsWindowHandle = nullptr;
|
||||
KeyboardManagerState* ShortcutControl::keyboardManagerState = nullptr;
|
||||
// Initialized as new vector
|
||||
std::vector<std::pair<std::vector<Shortcut>, std::wstring>> ShortcutControl::shortcutRemapBuffer;
|
||||
std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>> ShortcutControl::shortcutRemapBuffer;
|
||||
|
||||
ShortcutControl::ShortcutControl(Grid table, const int colIndex, TextBox targetApp)
|
||||
{
|
||||
shortcutDropDownStackPanel = StackPanel();
|
||||
typeShortcut = Button();
|
||||
shortcutControlLayout = StackPanel();
|
||||
bool isHybridControl = colIndex == 1 ? true : false;
|
||||
|
||||
shortcutDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
shortcutDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
|
||||
typeShortcut.as<Button>().Content(winrt::box_value(L"Type Shortcut"));
|
||||
typeShortcut.as<Button>().Content(winrt::box_value(L"Type"));
|
||||
typeShortcut.as<Button>().Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
|
||||
typeShortcut.as<Button>().Click([&, table, colIndex, targetApp](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
typeShortcut.as<Button>().Click([&, table, colIndex, isHybridControl, targetApp](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowActivated, EditShortcutsWindowHandle);
|
||||
// Using the XamlRoot of the typeShortcut to get the root of the XAML host
|
||||
createDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), shortcutRemapBuffer, *keyboardManagerState, colIndex, table, targetApp);
|
||||
createDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, shortcutControlLayout.as<StackPanel>(), targetApp, isHybridControl, false, EditShortcutsWindowHandle, shortcutRemapBuffer);
|
||||
});
|
||||
|
||||
shortcutControlLayout.as<StackPanel>().Margin({ 0, 0, 0, 10 });
|
||||
@@ -31,12 +33,12 @@ ShortcutControl::ShortcutControl(Grid table, const int colIndex, TextBox targetA
|
||||
|
||||
shortcutControlLayout.as<StackPanel>().Children().Append(typeShortcut.as<Button>());
|
||||
shortcutControlLayout.as<StackPanel>().Children().Append(shortcutDropDownStackPanel.as<StackPanel>());
|
||||
KeyDropDownControl::AddDropDown(table, shortcutControlLayout.as<StackPanel>(), shortcutDropDownStackPanel.as<StackPanel>(), colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp);
|
||||
KeyDropDownControl::AddDropDown(table, shortcutControlLayout.as<StackPanel>(), shortcutDropDownStackPanel.as<StackPanel>(), colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, false);
|
||||
shortcutControlLayout.as<StackPanel>().UpdateLayout();
|
||||
}
|
||||
|
||||
// Function to add a new row to the shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values.
|
||||
void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, Shortcut originalKeys, Shortcut newKeys, std::wstring targetAppName)
|
||||
void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys, const std::variant<DWORD, Shortcut>& newKeys, const std::wstring& targetAppName)
|
||||
{
|
||||
// Textbox for target application
|
||||
TextBox targetAppTextBox;
|
||||
@@ -87,12 +89,26 @@ void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::ve
|
||||
int rowIndex = (lastIndexInRow - KeyboardManagerConstants::ShortcutTableHeaderCount) / KeyboardManagerConstants::ShortcutTableColCount;
|
||||
|
||||
// Validate both set of drop downs
|
||||
KeyDropDownControl::ValidateShortcutFromDropDownList(parent, keyboardRemapControlObjects[rowIndex][0]->getShortcutControl(), keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownStackPanel.as<StackPanel>(), 0, ShortcutControl::shortcutRemapBuffer, keyboardRemapControlObjects[rowIndex][0]->keyDropDownControlObjects, targetAppTextBox);
|
||||
KeyDropDownControl::ValidateShortcutFromDropDownList(parent, keyboardRemapControlObjects[rowIndex][1]->getShortcutControl(), keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>(), 1, ShortcutControl::shortcutRemapBuffer, keyboardRemapControlObjects[rowIndex][1]->keyDropDownControlObjects, targetAppTextBox);
|
||||
KeyDropDownControl::ValidateShortcutFromDropDownList(parent, keyboardRemapControlObjects[rowIndex][0]->getShortcutControl(), keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownStackPanel.as<StackPanel>(), 0, ShortcutControl::shortcutRemapBuffer, keyboardRemapControlObjects[rowIndex][0]->keyDropDownControlObjects, targetAppTextBox, false, false);
|
||||
KeyDropDownControl::ValidateShortcutFromDropDownList(parent, keyboardRemapControlObjects[rowIndex][1]->getShortcutControl(), keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>(), 1, ShortcutControl::shortcutRemapBuffer, keyboardRemapControlObjects[rowIndex][1]->keyDropDownControlObjects, targetAppTextBox, true, false);
|
||||
|
||||
// Reset the buffer based on the selected drop down items
|
||||
shortcutRemapBuffer[rowIndex].first[0].SetKeyCodes(KeyDropDownControl::GetKeysFromStackPanel(keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownStackPanel.as<StackPanel>()));
|
||||
shortcutRemapBuffer[rowIndex].first[1].SetKeyCodes(KeyDropDownControl::GetKeysFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>()));
|
||||
std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[0]).SetKeyCodes(KeyDropDownControl::GetKeysFromStackPanel(keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownStackPanel.as<StackPanel>()));
|
||||
// second column is a hybrid column
|
||||
std::vector<DWORD> selectedKeyCodes = KeyDropDownControl::GetKeysFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>());
|
||||
|
||||
// If exactly one key is selected consider it to be a key remap
|
||||
if (selectedKeyCodes.size() == 1)
|
||||
{
|
||||
shortcutRemapBuffer[rowIndex].first[1] = selectedKeyCodes[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
Shortcut tempShortcut;
|
||||
tempShortcut.SetKeyCodes(selectedKeyCodes);
|
||||
// Assign instead of setting the value in the buffer since the previous value may not be a Shortcut
|
||||
shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||
}
|
||||
std::wstring newText = targetAppTextBox.Text().c_str();
|
||||
std::wstring lowercaseDefAppName = KeyboardManagerConstants::DefaultAppName;
|
||||
std::transform(newText.begin(), newText.end(), newText.begin(), towlower);
|
||||
@@ -153,50 +169,33 @@ void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::ve
|
||||
parent.UpdateLayout();
|
||||
|
||||
// Set the shortcut text if the two vectors are not empty (i.e. default args)
|
||||
if (originalKeys.IsValidShortcut() && newKeys.IsValidShortcut())
|
||||
if (originalKeys.IsValidShortcut() && !(newKeys.index() == 0 && std::get<DWORD>(newKeys) == NULL) && !(newKeys.index() == 1 && !std::get<Shortcut>(newKeys).IsValidShortcut()))
|
||||
{
|
||||
// change to load app name
|
||||
shortcutRemapBuffer.push_back(std::make_pair<std::vector<Shortcut>, std::wstring>(std::vector<Shortcut>{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->AddShortcutToControl(originalKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->shortcutDropDownStackPanel.as<StackPanel>(), *keyboardManagerState, 0, targetAppTextBox);
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->AddShortcutToControl(newKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->shortcutDropDownStackPanel.as<StackPanel>(), *keyboardManagerState, 1, targetAppTextBox);
|
||||
shortcutRemapBuffer.push_back(std::make_pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>(std::vector<std::variant<DWORD, Shortcut>>{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
KeyDropDownControl::AddShortcutToControl(originalKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->shortcutDropDownStackPanel.as<StackPanel>(), *keyboardManagerState, 0, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects, shortcutRemapBuffer, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->shortcutControlLayout.as<StackPanel>(), targetAppTextBox, false, false);
|
||||
|
||||
if (newKeys.index() == 0)
|
||||
{
|
||||
std::vector<DWORD> shortcutListKeyCodes = keyboardManagerState->keyboardMap.GetKeyCodeList(true);
|
||||
auto it = std::find(shortcutListKeyCodes.begin(), shortcutListKeyCodes.end(), std::get<DWORD>(newKeys));
|
||||
if (it != shortcutListKeyCodes.end())
|
||||
{
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects[0]->SetSelectedIndex((int32_t)std::distance(shortcutListKeyCodes.begin(), it));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyDropDownControl::AddShortcutToControl(std::get<Shortcut>(newKeys), parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->shortcutDropDownStackPanel.as<StackPanel>(), *keyboardManagerState, 1, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects, shortcutRemapBuffer, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->shortcutControlLayout.as<StackPanel>(), targetAppTextBox, true, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize both shortcuts as empty shortcuts
|
||||
shortcutRemapBuffer.push_back(std::make_pair<std::vector<Shortcut>, std::wstring>(std::vector<Shortcut>{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
shortcutRemapBuffer.push_back(std::make_pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>(std::vector<std::variant<DWORD, Shortcut>>{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
}
|
||||
}
|
||||
|
||||
// Function to add a shortcut to the shortcut control as combo boxes
|
||||
void ShortcutControl::AddShortcutToControl(Shortcut& shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, TextBox targetApp)
|
||||
{
|
||||
// Delete the existing drop down menus
|
||||
parent.Children().Clear();
|
||||
// Remove references to the old drop down objects to destroy them
|
||||
keyDropDownControlObjects.clear();
|
||||
|
||||
std::vector<DWORD> shortcutKeyCodes = shortcut.GetKeyCodes();
|
||||
std::vector<DWORD> keyCodeList = keyboardManagerState.keyboardMap.GetKeyCodeList(true);
|
||||
if (shortcutKeyCodes.size() != 0)
|
||||
{
|
||||
KeyDropDownControl::AddDropDown(table, shortcutControlLayout.as<StackPanel>(), parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp);
|
||||
for (int i = 0; i < shortcutKeyCodes.size(); i++)
|
||||
{
|
||||
// New drop down gets added automatically when the SelectedIndex is set
|
||||
if (i < (int)parent.Children().Size())
|
||||
{
|
||||
ComboBox currentDropDown = parent.Children().GetAt(i).as<ComboBox>();
|
||||
auto it = std::find(keyCodeList.begin(), keyCodeList.end(), shortcutKeyCodes[i]);
|
||||
if (it != keyCodeList.end())
|
||||
{
|
||||
currentDropDown.SelectedIndex((int32_t)std::distance(keyCodeList.begin(), it));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
parent.UpdateLayout();
|
||||
}
|
||||
|
||||
// Function to return the stack panel element of the ShortcutControl. This is the externally visible UI element which can be used to add it to other layouts
|
||||
StackPanel ShortcutControl::getShortcutControl()
|
||||
{
|
||||
@@ -204,7 +203,7 @@ StackPanel ShortcutControl::getShortcutControl()
|
||||
}
|
||||
|
||||
// Function to create the detect shortcut UI window
|
||||
void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, KeyboardManagerState& keyboardManagerState, const int colIndex, Grid table, TextBox targetApp)
|
||||
void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState, const int colIndex, Grid table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& remapBuffer)
|
||||
{
|
||||
// ContentDialog for detecting shortcuts. This is the parent UI element.
|
||||
ContentDialog detectShortcutBox;
|
||||
@@ -215,7 +214,7 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
detectShortcutBox.IsPrimaryButtonEnabled(false);
|
||||
detectShortcutBox.IsSecondaryButtonEnabled(false);
|
||||
|
||||
// Get the linked text block for the "Type shortcut" button that was clicked
|
||||
// Get the linked stack panel for the "Type shortcut" button that was clicked
|
||||
StackPanel linkedShortcutStackPanel = KeyboardManagerHelper::getSiblingElement(sender).as<StackPanel>();
|
||||
|
||||
auto unregisterKeys = [&keyboardManagerState]() {
|
||||
@@ -230,33 +229,47 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
keyboardManagerState.ResetDetectedShortcutKey(key);
|
||||
};
|
||||
|
||||
auto onPressEnter = [this,
|
||||
linkedShortcutStackPanel,
|
||||
auto onPressEnter = [linkedShortcutStackPanel,
|
||||
detectShortcutBox,
|
||||
&keyboardManagerState,
|
||||
&shortcutRemapBuffer,
|
||||
unregisterKeys,
|
||||
colIndex,
|
||||
table,
|
||||
targetApp] {
|
||||
targetApp,
|
||||
&keyDropDownControlObjects,
|
||||
controlLayout,
|
||||
isHybridControl,
|
||||
isSingleKeyWindow,
|
||||
&remapBuffer] {
|
||||
// Save the detected shortcut in the linked text block
|
||||
Shortcut detectedShortcutKeys = keyboardManagerState.GetDetectedShortcut();
|
||||
|
||||
if (!detectedShortcutKeys.IsEmpty())
|
||||
{
|
||||
// The shortcut buffer gets set in this function
|
||||
AddShortcutToControl(detectedShortcutKeys, table, linkedShortcutStackPanel, keyboardManagerState, colIndex, targetApp);
|
||||
KeyDropDownControl::AddShortcutToControl(detectedShortcutKeys, table, linkedShortcutStackPanel, keyboardManagerState, colIndex, keyDropDownControlObjects, remapBuffer, controlLayout, targetApp, isHybridControl, isSingleKeyWindow);
|
||||
}
|
||||
// Hide the type shortcut UI
|
||||
detectShortcutBox.Hide();
|
||||
};
|
||||
|
||||
auto onReleaseEnter = [&keyboardManagerState,
|
||||
unregisterKeys] {
|
||||
unregisterKeys,
|
||||
isSingleKeyWindow,
|
||||
parentWindow] {
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
if (isSingleKeyWindow)
|
||||
{
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Revert UI state back to Edit Shortcut window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, EditShortcutsWindowHandle);
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
|
||||
}
|
||||
|
||||
unregisterKeys();
|
||||
};
|
||||
|
||||
@@ -303,11 +316,19 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
cancelButton.Margin({ 2, 2, 2, 2 });
|
||||
cancelButton.Content(cancelButtonText);
|
||||
// Cancel button
|
||||
cancelButton.Click([detectShortcutBox, unregisterKeys, &keyboardManagerState](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
cancelButton.Click([detectShortcutBox, unregisterKeys, &keyboardManagerState, isSingleKeyWindow, parentWindow](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
if (isSingleKeyWindow)
|
||||
{
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Revert UI state back to Edit Shortcut window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, EditShortcutsWindowHandle);
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
|
||||
}
|
||||
unregisterKeys();
|
||||
detectShortcutBox.Hide();
|
||||
});
|
||||
@@ -315,7 +336,7 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
keyboardManagerState.RegisterKeyDelay(
|
||||
VK_ESCAPE,
|
||||
selectDetectedShortcutAndResetKeys,
|
||||
[&keyboardManagerState, detectShortcutBox, unregisterKeys](DWORD) {
|
||||
[&keyboardManagerState, detectShortcutBox, unregisterKeys, isSingleKeyWindow, parentWindow](DWORD) {
|
||||
detectShortcutBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[detectShortcutBox] {
|
||||
@@ -323,8 +344,16 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
});
|
||||
|
||||
keyboardManagerState.ResetUIState();
|
||||
if (isSingleKeyWindow)
|
||||
{
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Revert UI state back to Edit Shortcut window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, EditShortcutsWindowHandle);
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
|
||||
}
|
||||
unregisterKeys();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "keyboardmanager/common/Shortcut.h"
|
||||
#include <variant>
|
||||
|
||||
class KeyboardManagerState;
|
||||
class KeyDropDownControl;
|
||||
@@ -32,7 +33,7 @@ public:
|
||||
// Pointer to the keyboard manager state
|
||||
static KeyboardManagerState* keyboardManagerState;
|
||||
// Stores the current list of remappings
|
||||
static std::vector<std::pair<std::vector<Shortcut>, std::wstring>> shortcutRemapBuffer;
|
||||
static std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>> shortcutRemapBuffer;
|
||||
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
|
||||
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
|
||||
|
||||
@@ -40,14 +41,11 @@ public:
|
||||
ShortcutControl(Grid table, const int colIndex, TextBox targetApp);
|
||||
|
||||
// Function to add a new row to the shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values.
|
||||
static void AddNewShortcutControlRow(Grid& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, Shortcut originalKeys = Shortcut(), Shortcut newKeys = Shortcut(), std::wstring targetAppName = L"");
|
||||
|
||||
// Function to add a shortcut to the shortcut control as combo boxes
|
||||
void AddShortcutToControl(Shortcut& shortcut, Grid table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, TextBox targetApp);
|
||||
static void AddNewShortcutControlRow(Grid& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys = Shortcut(), const std::variant<DWORD, Shortcut>& newKeys = Shortcut(), const std::wstring& targetAppName = L"");
|
||||
|
||||
// Function to return the stack panel element of the ShortcutControl. This is the externally visible UI element which can be used to add it to other layouts
|
||||
StackPanel getShortcutControl();
|
||||
|
||||
// Function to create the detect shortcut UI window
|
||||
void createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::pair<std::vector<Shortcut>, std::wstring>>& shortcutRemapBuffer, KeyboardManagerState& keyboardManagerState, const int colIndex, Grid table, TextBox targetApp);
|
||||
static void createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState, const int colIndex, Grid table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>>& remapBuffer);
|
||||
};
|
||||
|
||||
@@ -3,39 +3,64 @@
|
||||
#include "keyboardmanager/common/Helpers.h"
|
||||
#include "keyboardmanager/common/KeyboardManagerConstants.h"
|
||||
#include "keyboardmanager/common/KeyboardManagerState.h"
|
||||
|
||||
#include "ShortcutControl.h"
|
||||
|
||||
//Both static members are initialized to null
|
||||
HWND SingleKeyRemapControl::EditKeyboardWindowHandle = nullptr;
|
||||
KeyboardManagerState* SingleKeyRemapControl::keyboardManagerState = nullptr;
|
||||
// Initialized as new vector
|
||||
std::vector<std::vector<DWORD>> SingleKeyRemapControl::singleKeyRemapBuffer;
|
||||
std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>> SingleKeyRemapControl::singleKeyRemapBuffer;
|
||||
|
||||
SingleKeyRemapControl::SingleKeyRemapControl(Grid table, const int colIndex) :
|
||||
singleKeyRemapDropDown(false)
|
||||
SingleKeyRemapControl::SingleKeyRemapControl(Grid table, const int colIndex)
|
||||
{
|
||||
typeKey = Button();
|
||||
typeKey.as<Button>().Content(winrt::box_value(L"Type Key"));
|
||||
typeKey.as<Button>().Width(KeyboardManagerConstants::RemapTableDropDownWidth);
|
||||
typeKey.as<Button>().Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated, EditKeyboardWindowHandle);
|
||||
// Using the XamlRoot of the typeKey to get the root of the XAML host
|
||||
createDetectKeyWindow(sender, sender.as<Button>().XamlRoot(), singleKeyRemapBuffer, *keyboardManagerState);
|
||||
});
|
||||
typeKey.as<Button>().Content(winrt::box_value(L"Type"));
|
||||
|
||||
singleKeyRemapControlLayout = StackPanel();
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Margin({ 0, 0, 0, 10 });
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Spacing(10);
|
||||
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(typeKey.as<Button>());
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(singleKeyRemapDropDown.GetComboBox());
|
||||
|
||||
// Key column
|
||||
if (colIndex == 0)
|
||||
{
|
||||
keyDropDownControlObjects.push_back(std::move(std::unique_ptr<KeyDropDownControl>(new KeyDropDownControl(false))));
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(keyDropDownControlObjects[0]->GetComboBox());
|
||||
// Set selection handler for the drop down
|
||||
singleKeyRemapDropDown.SetSelectionHandler(table, singleKeyRemapControlLayout.as<StackPanel>(), colIndex, singleKeyRemapBuffer);
|
||||
keyDropDownControlObjects[0]->SetSelectionHandler(table, singleKeyRemapControlLayout.as<StackPanel>(), colIndex, singleKeyRemapBuffer);
|
||||
}
|
||||
|
||||
// Hybrid column
|
||||
else
|
||||
{
|
||||
hybridDropDownStackPanel = StackPanel();
|
||||
hybridDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
hybridDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
KeyDropDownControl::AddDropDown(table, singleKeyRemapControlLayout.as<StackPanel>(), hybridDropDownStackPanel.as<StackPanel>(), colIndex, singleKeyRemapBuffer, keyDropDownControlObjects, nullptr, true, true);
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(hybridDropDownStackPanel.as<StackPanel>());
|
||||
}
|
||||
|
||||
StackPanel controlStackPanel = singleKeyRemapControlLayout.as<StackPanel>();
|
||||
typeKey.as<Button>().Click([&, table, colIndex, controlStackPanel](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
// Using the XamlRoot of the typeKey to get the root of the XAML host
|
||||
if (colIndex == 0)
|
||||
{
|
||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated, EditKeyboardWindowHandle);
|
||||
createDetectKeyWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState);
|
||||
}
|
||||
else
|
||||
{
|
||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
||||
ShortcutControl::createDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, controlStackPanel, nullptr, true, true, EditKeyboardWindowHandle, singleKeyRemapBuffer);
|
||||
}
|
||||
});
|
||||
|
||||
singleKeyRemapControlLayout.as<StackPanel>().UpdateLayout();
|
||||
}
|
||||
|
||||
// Function to add a new row to the remap keys table. If the originalKey and newKey args are provided, then the displayed remap keys are set to those values.
|
||||
void SingleKeyRemapControl::AddNewControlKeyRemapRow(Grid& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey, const DWORD newKey)
|
||||
void SingleKeyRemapControl::AddNewControlKeyRemapRow(Grid& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey, const std::variant<DWORD, Shortcut> newKey)
|
||||
{
|
||||
// Create new SingleKeyRemapControl objects dynamically so that we does not get destructed
|
||||
std::vector<std::unique_ptr<SingleKeyRemapControl>> newrow;
|
||||
@@ -66,25 +91,33 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(Grid& parent, std::vector<s
|
||||
parent.Children().Append(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->getSingleKeyRemapControl());
|
||||
|
||||
// Set the key text if the two keys are not null (i.e. default args)
|
||||
if (originalKey != NULL && newKey != NULL)
|
||||
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !std::get<Shortcut>(newKey).IsValidShortcut()))
|
||||
{
|
||||
singleKeyRemapBuffer.push_back(std::vector<DWORD>{ originalKey, newKey });
|
||||
singleKeyRemapBuffer.push_back(std::make_pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>(std::vector<std::variant<DWORD, Shortcut>>{ originalKey, newKey }, L""));
|
||||
std::vector<DWORD> keyCodes = keyboardManagerState->keyboardMap.GetKeyCodeList();
|
||||
std::vector<DWORD> shortcutListKeyCodes = keyboardManagerState->keyboardMap.GetKeyCodeList(true);
|
||||
auto it = std::find(keyCodes.begin(), keyCodes.end(), originalKey);
|
||||
if (it != keyCodes.end())
|
||||
{
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->singleKeyRemapDropDown.SetSelectedIndex((int32_t)std::distance(keyCodes.begin(), it));
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects[0]->SetSelectedIndex((int32_t)std::distance(keyCodes.begin(), it));
|
||||
}
|
||||
it = std::find(keyCodes.begin(), keyCodes.end(), newKey);
|
||||
if (it != keyCodes.end())
|
||||
if (newKey.index() == 0)
|
||||
{
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->singleKeyRemapDropDown.SetSelectedIndex((int32_t)std::distance(keyCodes.begin(), it));
|
||||
it = std::find(shortcutListKeyCodes.begin(), shortcutListKeyCodes.end(), std::get<DWORD>(newKey));
|
||||
if (it != shortcutListKeyCodes.end())
|
||||
{
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects[0]->SetSelectedIndex((int32_t)std::distance(shortcutListKeyCodes.begin(), it));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyDropDownControl::AddShortcutToControl(std::get<Shortcut>(newKey), parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->hybridDropDownStackPanel.as<StackPanel>(), *keyboardManagerState, 1, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects, singleKeyRemapBuffer, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->singleKeyRemapControlLayout.as<StackPanel>(), nullptr, true, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize both keys to NULL
|
||||
singleKeyRemapBuffer.push_back(std::vector<DWORD>{ NULL, NULL });
|
||||
singleKeyRemapBuffer.push_back(std::make_pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>(std::vector<std::variant<DWORD, Shortcut>>{ NULL, NULL }, L""));
|
||||
}
|
||||
|
||||
// Delete row button
|
||||
@@ -136,7 +169,7 @@ StackPanel SingleKeyRemapControl::getSingleKeyRemapControl()
|
||||
}
|
||||
|
||||
// Function to create the detect remap key UI window
|
||||
void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::vector<DWORD>>& singleKeyRemapBuffer, KeyboardManagerState& keyboardManagerState)
|
||||
void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState)
|
||||
{
|
||||
// ContentDialog for detecting remap key. This is the parent UI element.
|
||||
ContentDialog detectRemapKeyBox;
|
||||
@@ -160,7 +193,6 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
|
||||
auto onPressEnter = [linkedRemapDropDown,
|
||||
detectRemapKeyBox,
|
||||
&keyboardManagerState,
|
||||
&singleKeyRemapBuffer,
|
||||
unregisterKeys] {
|
||||
// Save the detected key in the linked text block
|
||||
DWORD detectedKey = keyboardManagerState.GetDetectedSingleRemapKey();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
#include "KeyDropDownControl.h"
|
||||
#include <keyboardmanager/common/Shortcut.h>
|
||||
#include <variant>
|
||||
|
||||
class KeyboardManagerState;
|
||||
namespace winrt::Windows::UI::Xaml
|
||||
@@ -15,32 +17,34 @@ namespace winrt::Windows::UI::Xaml
|
||||
class SingleKeyRemapControl
|
||||
{
|
||||
private:
|
||||
// Drop down to display the selected remap key
|
||||
KeyDropDownControl singleKeyRemapDropDown;
|
||||
|
||||
// Button to type the remap key
|
||||
winrt::Windows::Foundation::IInspectable typeKey;
|
||||
|
||||
// StackPanel to parent the above controls
|
||||
winrt::Windows::Foundation::IInspectable singleKeyRemapControlLayout;
|
||||
|
||||
// Stack panel for the drop downs to display the selected shortcut for the hybrid case
|
||||
winrt::Windows::Foundation::IInspectable hybridDropDownStackPanel;
|
||||
|
||||
public:
|
||||
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
|
||||
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
|
||||
// Handle to the current Edit Keyboard Window
|
||||
static HWND EditKeyboardWindowHandle;
|
||||
// Pointer to the keyboard manager state
|
||||
static KeyboardManagerState* keyboardManagerState;
|
||||
// Stores the current list of remappings
|
||||
static std::vector<std::vector<DWORD>> singleKeyRemapBuffer;
|
||||
static std::vector<std::pair<std::vector<std::variant<DWORD, Shortcut>>, std::wstring>> singleKeyRemapBuffer;
|
||||
|
||||
// constructor
|
||||
SingleKeyRemapControl(Grid table, const int colIndex);
|
||||
|
||||
// Function to add a new row to the remap keys table. If the originalKey and newKey args are provided, then the displayed remap keys are set to those values.
|
||||
static void AddNewControlKeyRemapRow(winrt::Windows::UI::Xaml::Controls::Grid& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey = NULL, const DWORD newKey = NULL);
|
||||
static void AddNewControlKeyRemapRow(winrt::Windows::UI::Xaml::Controls::Grid& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey = NULL, const std::variant<DWORD, Shortcut> newKey = NULL);
|
||||
|
||||
// Function to return the stack panel element of the SingleKeyRemapControl. This is the externally visible UI element which can be used to add it to other layouts
|
||||
winrt::Windows::UI::Xaml::Controls::StackPanel getSingleKeyRemapControl();
|
||||
|
||||
// Function to create the detect remap keys UI window
|
||||
void createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, std::vector<std::vector<DWORD>>& singleKeyRemapBuffer, KeyboardManagerState& keyboardManagerState);
|
||||
void createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user