diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp
index 2bf9d302a79a..04bb461a5eeb 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp
+++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp
@@ -228,6 +228,52 @@ namespace KeyboardEventHandlers
for (auto& itShortcut : state.GetSortedShortcutRemapVector(activatedApp))
{
const auto it = reMap.find(itShortcut);
+ static bool isAltRightKeyInvoked = false;
+
+ // Release key and delete from previous modifier key vector
+ if ((Helpers::IsModifierKey(data->lParam->vkCode) && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)))
+ {
+ std::vector keyEventList;
+ if (!(isAltRightKeyInvoked && data->lParam->vkCode == VK_LCONTROL))
+ {
+ Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(data->lParam->vkCode), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
+ state.ResetPreviousModifierKey(data->lParam->vkCode);
+ }
+
+ if (isAltRightKeyInvoked && data->lParam->vkCode == VK_RMENU && state.FindPreviousModifierKey(VK_LCONTROL))
+ {
+ Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, VK_LCONTROL, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
+ state.ResetPreviousModifierKey(it->first.GetCtrlKey());
+ }
+
+ if (data->lParam->vkCode == VK_RWIN || data->lParam->vkCode == VK_LWIN)
+ {
+ it->second.winKeyInvoked = ModifierKey::Disabled;
+ }
+
+ ii.SendVirtualInput(keyEventList);
+
+ }
+ else if ((Helpers::IsModifierKey(data->lParam->vkCode) && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)))
+ {
+ // Remember which win key was pressed initially
+ if (data->lParam->vkCode == VK_RWIN)
+ {
+ it->second.winKeyInvoked = ModifierKey::Right;
+ }
+ else if (data->lParam->vkCode == VK_LWIN)
+ {
+ it->second.winKeyInvoked = ModifierKey::Left;
+ }
+
+ // Set the previous modifier key of the invoked shortcut
+ SetPreviousModifierKey(it, data->lParam->vkCode, state);
+ // Check if the right Alt key (AltGr) is pressed.
+ if (data->lParam->vkCode == VK_RMENU && state.FindPreviousModifierKey(VK_LCONTROL))
+ {
+ isAltRightKeyInvoked = true;
+ }
+ }
// If a shortcut is currently in the invoked state then skip till the shortcut that is currently invoked
if (isShortcutInvoked && !it->second.isShortcutInvoked)
@@ -235,6 +281,12 @@ namespace KeyboardEventHandlers
continue;
}
+ // If action key is pressed check modifier key from shortcut with previous modifier key saved at state
+ if ((data->lParam->vkCode == it->first.GetActionKey()) && (state.GetPreviousModifierKey().size() == 0 || !CheckPreviousModifierKey(it, state)))
+ {
+ continue;
+ }
+
// Check if the remap is to a key or a shortcut
const bool remapToKey = it->second.targetShortcut.index() == 0;
const bool remapToShortcut = it->second.targetShortcut.index() == 1;
@@ -247,14 +299,6 @@ namespace KeyboardEventHandlers
bool isMatchOnChordEnd = false;
bool isMatchOnChordStart = false;
- static bool isAltRightKeyInvoked = false;
-
- // Check if the right Alt key (AltGr) is pressed.
- if (data->lParam->vkCode == VK_RMENU && ii.GetVirtualKeyState(VK_LCONTROL))
- {
- isAltRightKeyInvoked = true;
- }
-
// If the shortcut has been pressed down
if (!it->second.isShortcutInvoked && it->first.CheckModifiersKeyboardState(ii))
{
@@ -310,16 +354,6 @@ namespace KeyboardEventHandlers
std::vector keyEventList;
- // Remember which win key was pressed initially
- if (ii.GetVirtualKeyState(VK_RWIN))
- {
- it->second.winKeyInvoked = ModifierKey::Right;
- }
- else if (ii.GetVirtualKeyState(VK_LWIN))
- {
- it->second.winKeyInvoked = ModifierKey::Left;
- }
-
if (isRunProgram)
{
auto threadFunction = [it]() {
@@ -648,22 +682,13 @@ namespace KeyboardEventHandlers
if (!remapToText && ((!it->first.HasChord() && data->lParam->vkCode == it->first.GetActionKey()) || (it->first.HasChord() && data->lParam->vkCode == it->first.GetSecondKey())) && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP))
{
std::vector keyEventList;
- if (remapToShortcut && !it->first.HasChord())
- {
- // Just lift the action key for no chords.
- Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
- }
- else if (remapToShortcut && it->first.HasChord())
+ if (remapToShortcut)
{
- // If it has a chord, we'll want a full clean contemplated in the else, since you can't really repeat chords by pressing the end key again.
-
- // Key up for all new shortcut keys, key down for original shortcut modifiers and current key press but common keys aren't repeated
+ // Just lift the action key.
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
- // Release new shortcut state (release in reverse order of shortcut to be accurate)
Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
- // Set old shortcut key down state
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get(it->second.targetShortcut));
// Reset the remap state
@@ -711,7 +736,7 @@ namespace KeyboardEventHandlers
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->V, press Shift+Win+A and release A, since Win will be pressed here we need to send a dummy event after it
Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
- if (!isAltRightKeyInvoked)
+ if (!isAltRightKeyInvoked || (isAltRightKeyInvoked && data->lParam->vkCode != VK_LCONTROL))
{
// Reset the remap state
it->second.isShortcutInvoked = false;
@@ -848,7 +873,7 @@ namespace KeyboardEventHandlers
// Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after shortcut to shortcut is released to open start menu
}
- if (!isAltRightKeyInvoked)
+ if (!isAltRightKeyInvoked || (isAltRightKeyInvoked && data->lParam->vkCode != VK_LCONTROL))
{
// Reset the remap state
it->second.isShortcutInvoked = false;
@@ -899,7 +924,7 @@ namespace KeyboardEventHandlers
{
std::vector keyEventList;
- if (!isAltRightKeyInvoked)
+ if (!isAltRightKeyInvoked || (isAltRightKeyInvoked && data->lParam->vkCode != VK_LCONTROL))
{
// Set original shortcut key down state
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
@@ -917,7 +942,7 @@ namespace KeyboardEventHandlers
// Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after another shortcut to key remap is released to open start menu
- if (!isAltRightKeyInvoked)
+ if (!isAltRightKeyInvoked || (isAltRightKeyInvoked && data->lParam->vkCode != VK_LCONTROL))
{
// Reset the remap state
it->second.isShortcutInvoked = false;
@@ -1727,4 +1752,104 @@ namespace KeyboardEventHandlers
return 1;
}
+
+ bool CheckPreviousModifierKey(const ShortcutRemapTable::iterator it, State& state)
+ {
+ auto previousKeys = state.GetPreviousModifierKey();
+ if (!previousKeys.empty())
+ {
+ if (it->first.GetShiftKey() != 0)
+ {
+ if (!state.FindPreviousModifierKey(it->first.GetShiftKey()))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ for (auto key : previousKeys)
+ {
+ if ((VK_SHIFT == key) || (VK_LSHIFT == key) || (VK_RSHIFT == key))
+ {
+ return false;
+ }
+ }
+ }
+
+ if (it->first.GetAltKey() != 0)
+ {
+ if (!state.FindPreviousModifierKey(it->first.GetAltKey()))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ for (auto key : previousKeys)
+ {
+ if ((VK_MENU == key) || (VK_LMENU == key) || (VK_RMENU == key))
+ {
+ return false;
+ }
+ }
+ }
+
+ if (it->first.GetCtrlKey() != 0)
+ {
+ if (!state.FindPreviousModifierKey(it->first.GetCtrlKey()))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ for (auto key : previousKeys)
+ {
+ if ((VK_CONTROL == key) || (VK_LCONTROL == key) || (VK_RCONTROL == key))
+ {
+ return false;
+ }
+ }
+ }
+
+ if (it->first.GetWinKey(it->second.winKeyInvoked) != 0)
+ {
+ if (!state.FindPreviousModifierKey(it->first.GetWinKey(it->second.winKeyInvoked)))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ for (auto key : previousKeys)
+ {
+ if ((VK_LWIN == key) || (VK_RWIN == key))
+ {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ void SetPreviousModifierKey(const ShortcutRemapTable::iterator it, const DWORD key, State& state)
+ {
+ if (it->first.GetWinKey(it->second.winKeyInvoked) == key)
+ {
+ state.SetPreviousModifierKey(it->first.GetWinKey(it->second.winKeyInvoked));
+ }
+ else if (it->first.GetCtrlKey() == key)
+ {
+ state.SetPreviousModifierKey(it->first.GetCtrlKey());
+ }
+ else if (it->first.GetAltKey() == key)
+ {
+ state.SetPreviousModifierKey(it->first.GetAltKey());
+ }
+ else if (it->first.GetShiftKey() == key)
+ {
+ state.SetPreviousModifierKey(it->first.GetShiftKey());
+ }
+ }
}
diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h
index 67eeed69775f..67ca79468fd3 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h
+++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h
@@ -84,4 +84,10 @@ namespace KeyboardEventHandlers
// 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(KeyboardManagerInput::InputInterface& ii, DWORD key, DWORD target);
+
+ // Function to check previous modifier key with state
+ bool CheckPreviousModifierKey(const ShortcutRemapTable::iterator it, State& state);
+
+ // Function to set previous modifier key to state
+ void SetPreviousModifierKey(const ShortcutRemapTable::iterator it, const DWORD key, State& state);
};
diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/State.cpp b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/State.cpp
index e56b1dc0e6a9..d004d6b163d9 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/State.cpp
+++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/State.cpp
@@ -73,3 +73,30 @@ std::wstring State::GetActivatedApp()
{
return activatedAppSpecificShortcutTarget;
}
+
+// Sets the previous modifier key to check in another shortcut
+void State::SetPreviousModifierKey(const DWORD prevKey)
+{
+ if (!FindPreviousModifierKey(prevKey))
+ {
+ previousModifierKey.emplace_back(prevKey);
+ }
+}
+
+// Gets the previous modifier key
+std::vector State::GetPreviousModifierKey()
+{
+ return previousModifierKey;
+}
+
+// Check if a key exists in the previousModifierKey vector
+bool State::FindPreviousModifierKey(const DWORD prevKey)
+{
+ return std::find(previousModifierKey.begin(), previousModifierKey.end(), prevKey) != previousModifierKey.end();
+}
+
+// Resets the previous modifier key
+void State::ResetPreviousModifierKey(const DWORD prevKey)
+{
+ previousModifierKey.erase(std::remove(previousModifierKey.begin(), previousModifierKey.end(), prevKey), previousModifierKey.end());
+}
diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/State.h b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/State.h
index 53b476302ad1..d5b8632f1426 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/State.h
+++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/State.h
@@ -6,6 +6,8 @@ class State : public MappingConfiguration
private:
// Stores the activated target application in app-specific shortcut
std::wstring activatedAppSpecificShortcutTarget;
+ // Stores the previous modifier key
+ std::vector previousModifierKey;
public:
// Function to get the iterator of a single key remap given the source key. Returns nullopt if it isn't remapped
@@ -26,4 +28,16 @@ class State : public MappingConfiguration
// Gets the activated target application in app-specific shortcut
std::wstring GetActivatedApp();
+
+ // Sets the previous modifier key to check in another shortcut
+ void SetPreviousModifierKey(const DWORD prevKey);
+
+ // Gets the previous modifier key
+ std::vector GetPreviousModifierKey();
+
+ // Check a key if exist in previous modifier key vector
+ bool FindPreviousModifierKey(const DWORD prevKey);
+
+ // Resets the previous modifier key
+ void ResetPreviousModifierKey(const DWORD prevKey);
};
\ No newline at end of file
diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineTest/OSLevelShortcutRemappingTests.cpp b/src/modules/keyboardmanager/KeyboardManagerEngineTest/OSLevelShortcutRemappingTests.cpp
index 4b470ec5a6e0..33c17b6af327 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEngineTest/OSLevelShortcutRemappingTests.cpp
+++ b/src/modules/keyboardmanager/KeyboardManagerEngineTest/OSLevelShortcutRemappingTests.cpp
@@ -861,7 +861,7 @@ namespace RemappingLogicTests
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
}
- // Test if target modifier is still held down even if the action key of the original shortcut is released - required for Alt+Tab/Win+Space cases
+ // Test if original modifier is still held down even if the action key of the original shortcut is released - required for Alt+Tab/Win+Space cases
TEST_METHOD (RemappedShortcutModifiers_ShouldBeDetectedAsPressed_OnReleasingActionKeyButHoldingModifiers)
{
// Remap Ctrl+A to Alt+Tab
@@ -882,10 +882,10 @@ namespace RemappingLogicTests
// Send Ctrl+A, release A
mockedInputHandler.SendVirtualInput(inputs);
- // Ctrl, A, Tab key states should be unchanged, Alt should be true
- Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
+ // Alt, A, Tab key states should be unchanged, Ctrl should be true
+ Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
- Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), true);
+ Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_TAB), false);
}